File Coverage

blib/lib/Data/Dict.pm
Criterion Covered Total %
statement 70 105 66.6
branch 12 44 27.2
condition 2 3 66.6
subroutine 20 30 66.6
pod 25 25 100.0
total 129 207 62.3


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