File Coverage

blib/lib/Data/Dict.pm
Criterion Covered Total %
statement 66 87 75.8
branch 14 20 70.0
condition 2 3 66.6
subroutine 19 25 76.0
pod 20 20 100.0
total 121 155 78.0


line stmt bran cond sub pod time code
1             package Data::Dict;
2              
3 1     1   65041 use strict;
  1         11  
  1         29  
4 1     1   5 use warnings;
  1         2  
  1         39  
5 1     1   6 use Carp ();
  1         2  
  1         15  
6 1     1   4 use Exporter 'import';
  1         2  
  1         65  
7              
8 1     1   5 use constant HAS_COLLECTION => !!eval { require Mojo::Collection; 1 };
  1         2  
  1         1  
  1         1394  
  0         0  
9              
10             our $VERSION = '0.001';
11              
12             our @EXPORT_OK = 'd';
13              
14 22     22 1 2266 sub d { __PACKAGE__->new(@_) }
15              
16             sub new {
17 47     47 1 89 my $class = shift;
18 47   66     314 return bless {@_}, ref $class || $class;
19             }
20              
21 0     0 1 0 sub TO_JSON { +{%{$_[0]}} }
  0         0  
22              
23             sub delete {
24 9     9 1 32 my $self = shift;
25 9         22 my $deleted = $self->slice(@_);
26 9 100       48 delete @$self{keys %$deleted} if keys %$deleted;
27 9         30 return $deleted;
28             }
29              
30             sub each {
31 3     3 1 528 my ($self, $cb) = @_;
32 3 100       15 return map { [$_, $self->{$_}] } sort keys %$self unless $cb;
  3         13  
33 2         13 $cb->($_, $self->{$_}) for sort keys %$self;
34 2         18 return $self;
35             }
36              
37             sub each_c {
38 0     0 1 0 Carp::croak 'Mojo::Collection is required for each_c' unless HAS_COLLECTION;
39 0         0 my $self = shift;
40 0 0       0 return Mojo::Collection->new(map { Mojo::Collection->new(@$_) } $self->each) unless @_;
  0         0  
41 0         0 return $self->each(@_);
42             }
43              
44             sub extract {
45 4     4 1 17 my ($self, $cb) = @_;
46 4 100       18 return $self->delete(grep { m/$cb/ } sort keys %$self) if ref $cb eq 'Regexp';
  9         30  
47 3         13 return $self->delete(grep { $cb->($_, $self->{$_}) } sort keys %$self);
  7         20  
48             }
49              
50             sub grep {
51 7     7 1 23 my ($self, $cb) = @_;
52 7 100       25 return $self->new(map { ($_, $self->{$_}) } grep { m/$cb/ } sort keys %$self) if ref $cb eq 'Regexp';
  4         11  
  9         29  
53 6         33 return $self->new(map { ($_, $self->{$_}) } grep { $cb->($_, $self->{$_}) } sort keys %$self);
  13         34  
  54         153  
54             }
55              
56             sub map {
57 2     2 1 1045 my ($self, $cb) = @_;
58 2         11 return map { $cb->($_, $self->{$_}) } sort keys %$self;
  6         22  
59             }
60              
61             sub map_c {
62 0     0 1 0 Carp::croak 'Mojo::Collection is required for map_c' unless HAS_COLLECTION;
63 0         0 my $self = shift;
64 0         0 return Mojo::Collection->new($self->map(@_));
65             }
66              
67 6     6 1 26 sub size { scalar keys %{$_[0]} }
  6         32  
68              
69             sub slice {
70 16     16 1 30 my $self = shift;
71 16         30 return $self->new(map { ($_, $self->{$_}) } @_);
  29         73  
72             }
73              
74             sub tap {
75 1     1 1 4 my ($self, $cb) = (shift, shift);
76 1         5 $_->$cb(@_) for $self;
77 1         6 return $self;
78             }
79              
80 0     0 1 0 sub to_array { [%{$_[0]}] }
  0         0  
81              
82 24     24 1 38 sub to_hash { +{%{$_[0]}} }
  24         155  
83              
84             sub transform {
85 2     2 1 15 my ($self, $cb) = @_;
86 2         11 return $self->new(map { $cb->($_, $self->{$_}); } sort keys %$self);
  6         24  
87             }
88              
89             sub values {
90 5     5 1 19 my ($self, $cb) = (shift, shift);
91 5 100       45 return map { $self->{$_} } sort keys %$self unless $cb;
  6         18  
92 1         5 do { $_->$cb(@_) for $self->{$_} } for sort keys %$self;
  3         14  
93 1         4 return $self;
94             }
95              
96             sub values_c {
97 0     0 1 0 Carp::croak 'Mojo::Collection is required for values_c' unless HAS_COLLECTION;
98 0         0 my $self = shift;
99 0 0       0 return Mojo::Collection->new($self->values) unless @_;
100 0         0 return $self->values(@_);
101             }
102              
103             # define this last because CORE::keys doesn't work before 5.20
104             sub keys {
105 5     5 1 18 my ($self, $cb) = @_;
106 5 100       40 return wantarray ? sort keys %$self : keys %$self unless $cb;
    100          
107 1         6 $cb->($_) for sort keys %$self;
108 1         9 return $self;
109             }
110              
111             sub keys_c {
112 0     0 1   Carp::croak 'Mojo::Collection is required for keys_c' unless HAS_COLLECTION;
113 0           my $self = shift;
114 0 0         return Mojo::Collection->new($self->keys) unless @_;
115 0           return $self->keys(@_);
116             }
117              
118             1;
119              
120             =head1 NAME
121              
122             Data::Dict - Hash-based dictionary object
123              
124             =head1 SYNOPSIS
125              
126             use Data::Dict;
127              
128             # Manipulate dictionary
129             my $dictionary = Data::Dict->new(a => 1, b => 2, c => 3);
130             delete $dictionary->{b};
131             print join "\n", $dictionary->keys;
132              
133             # Chain methods
134             $dictionary->slice(qw(a b))->grep(sub { defined $_[1] })->each(sub {
135             my ($key, $value) = @_;
136             print "$key: $value\n";
137             });
138              
139             # Use the alternative constructor
140             use Data::Dict 'd';
141             use experimental 'signatures';
142             my $num_highest = d(%counts)->transform(sub ($k, $v) { ($k, $v+1) })->grep(sub ($k, $v) { $v > 5 })->size;
143              
144             # Use Mojo::Collection for more chaining
145             d(%hash)->map_c(sub { join ':', @_ })->shuffle->join("\n")->say;
146              
147             =head1 DESCRIPTION
148              
149             L is a hash-based container for dictionaries, with heavy
150             inspiration from L.
151              
152             # Access hash directly to manipulate dictionary
153             my $dict = Data::Dict->new(a => 1, b => 2, c => 3);
154             $dict->{b} += 100;
155             print "$_\n" for values %$dict;
156              
157             =head1 FUNCTIONS
158              
159             =head2 d
160              
161             my $dict = d(a => 1, b => 2);
162              
163             Construct a new hash-based L object. Exported on demand.
164              
165             =head1 METHODS
166              
167             =head2 new
168              
169             my $dict = Data::Dict->new(a => 1, b => 2);
170              
171             Construct a new hash-based L object.
172              
173             =head2 TO_JSON
174              
175             Alias for L.
176              
177             =head2 delete
178              
179             my $deleted = $dict->delete(@keys);
180              
181             Delete selected keys from the dictionary and return a new dictionary containing
182             the deleted keys and values.
183              
184             =head2 each
185              
186             my @pairs = $dict->each;
187             $dict = $dict->each(sub {...});
188              
189             Evaluate callback for each pair in the dictionary in sorted-key order, or
190             return pairs as list of key/value arrayrefs in sorted-key order if none has
191             been provided. The callback will receive the key and value as arguments.
192              
193             $dict->each(sub {
194             my ($key, $value) = @_;
195             print "$key: $value\n";
196             });
197              
198             # values can be modified in place
199             $dict->each(sub { $_[1] = $_[0]x2 });
200              
201             =head2 each_c
202              
203             my $collection = $dict->each_c;
204              
205             Create a new collection of key/value pairs as collections in sorted-key order.
206             Requires L.
207              
208             # print all keys and values
209             print $dict->each_c->flatten->join(' ');
210              
211             =head2 extract
212              
213             my $extracted = $dict->extract(qr/foo/);
214             my $extracted = $dict->extract(sub {...});
215              
216             Evaluate regular expression on each key, or call callback on each key/value
217             pair in the dictionary in sorted-key order, and remove all pairs that matched
218             the regular expression, or for which the callback returned true. Return a new
219             dictionary with the removed keys and values. The callback will receive the key
220             and value as arguments.
221              
222             my $high_numbers = $dict->extract(sub { $_[1] > 100 });
223              
224             =head2 grep
225              
226             my $new = $dict->grep(qr/foo/);
227             my $new = $dict->grep(sub {...});
228              
229             Evaluate regular expression on each key, or call callback on each key/value
230             pair in the dictionary in sorted-key order, and return a new dictionary with
231             all pairs that matched the regular expression, or for which the callback
232             returned true. The callback will receive the key and value as arguments.
233              
234             my $banana_dict = $dict->grep(qr/banana/);
235              
236             my $fruits_dict = $dict->grep(sub { $_[1]->isa('Fruit') });
237              
238             =head2 keys
239              
240             my @keys = $dict->keys;
241             $dict = $dict->keys(sub {...});
242              
243             Evaluate callback for each key in the dictionary in sorted-key order, or return
244             all keys as a sorted list if none has been provided. The key will be the first
245             argument passed to the callback, and is also available as C<$_>.
246              
247             =head2 keys_c
248              
249             my $collection = $dict->keys_c;
250              
251             Create a new collection from sorted keys. Requires L.
252              
253             my $first_key = $dict->keys_c->first;
254              
255             =head2 map
256              
257             my @results = $dict->map(sub {...});
258              
259             Evaluate callback for each key/value pair in the dictionary in sorted-key order
260             and return the results as a list. The callback will receive the key and value
261             as arguments.
262              
263             my @pairs = $dict->map(sub { [@_] });
264              
265             my @values = $dict->map(sub { $_[1] });
266              
267             =head2 map_c
268              
269             my $collection = $dict->map_c(sub {...});
270              
271             Evaluate callback for each key/value pair in the dictionary in sorted-key order
272             and create a new collection from the results. The callback will receive the key
273             and value as arguments. Requires L.
274              
275             my $output = $dict->map_c(sub { "$_[0]: $_[1]" })->join("\n");
276              
277             =head2 size
278              
279             my $size = $dict->size;
280              
281             Number of keys in dictionary.
282              
283             =head2 slice
284              
285             my $new = $dict->slice(@keys);
286              
287             Create a new dictionary with all selected keys.
288              
289             print join ' ', d(a => 1, b => 2, c => 3)->slice('a', 'c')->map(sub { join ':', @_ });
290             # a:1 c:3
291              
292             =head2 tap
293              
294             $dict = $dict->tap(sub {...});
295              
296             Perform callback and return the dictionary object for further chaining, as in
297             L. The dictionary object will be the first argument passed to
298             the callback, and is also available as C<$_>.
299              
300             =head2 to_array
301              
302             my $array = $dict->to_array;
303              
304             Turn dictionary into even-sized array reference in sorted-key order.
305              
306             =head2 to_hash
307              
308             my $hash = $dict->to_hash;
309              
310             Turn dictionary into hash reference.
311              
312             =head2 transform
313              
314             my $new = $dict->transform(sub {...});
315              
316             Evaluate callback for each key/value pair in the dictionary in sorted-key order
317             and create a new dictionary from the returned keys and values (assumed to be an
318             even-sized key/value list). The callback will receive the key and value as
319             arguments.
320              
321             my $reversed = $dict->transform(sub { ($_[1], $_[0]) });
322              
323             my $doubled = $dict->transform(sub {
324             my ($k, $v) = @_;
325             return ($k => $v, ${k}x2 => $v);
326             });
327              
328             =head2 values
329              
330             my @values = $dict->values;
331             $dict = $dict->values(sub {...});
332              
333             Evaluate callback for each value in the dictionary in sorted-key order, or
334             return all values as a list in sorted-key order if none has been provided. The
335             value will be the first argument passed to the callback, and is also available
336             as C<$_>.
337              
338             # values can be modified in place
339             $dict->values(sub { $_++ });
340              
341             =head2 values_c
342              
343             my $collection = $dict->values_c;
344              
345             Create a new collection from all values in sorted-key order. Requires
346             L.
347              
348             my @shuffled_values = $dict->values_c->shuffle->each;
349              
350             =head1 BUGS
351              
352             Report any issues on the public bugtracker.
353              
354             =head1 AUTHOR
355              
356             Dan Book
357              
358             =head1 COPYRIGHT AND LICENSE
359              
360             This software is Copyright (c) 2018 by Dan Book.
361              
362             This is free software, licensed under:
363              
364             The Artistic License 2.0 (GPL Compatible)
365              
366             =head1 SEE ALSO
367              
368             L