File Coverage

blib/lib/Data/Combination.pm
Criterion Covered Total %
statement 63 64 98.4
branch 15 16 93.7
condition n/a
subroutine 6 6 100.0
pod 1 3 33.3
total 85 89 95.5


line stmt bran cond sub pod time code
1             package Data::Combination;
2              
3              
4             =head1 NAME
5              
6             Data::Combination - Hash and Array element combination generator
7              
8             =head1 SYNOPIS
9              
10             use Data::Combination;
11            
12             #Generate combination of array with two 'fields'
13             my $result=Data::Combination::combinations([[1,2,3], [qw(a b c)])
14            
15             # $result is an array ref of all combinations of the two fields
16             [
17             [1,a],
18             [1,b],
19             [1,c],
20              
21             [2,a],
22             [2,b],
23             [2,c],
24              
25             [3,a],
26             [3,b],
27             [3,c]
28             ]
29              
30             #Generate combination of hash with two 'fields'
31             my $result=Data::Combination::combinations(key1=>[1,2,3], key2=>[qw(a b c)])
32              
33             # $result is an array ref of all combinations of the two fields
34             [
35             {key1=>1,key2=>a},
36             {key1=>2,key2=>a},
37             {key1=>3,key2=>a},
38            
39             {key1=>1,key2=>b},
40             {key1=>2,key2=>b},
41             {key1=>3,key2=>b},
42              
43             {key1=>1,key2=>c},
44             {key1=>2,key2=>c},
45             {key1=>3,key2=>c},
46             ]
47              
48             =head1 DESCRIPTION
49              
50             C generates hashes or arrays by making combinations of
51             values for keys with array values.
52              
53              
54             =head1 EXAMPLES
55              
56             Array examples:
57              
58             ===
59             input:
60             ["a","b","c"]
61              
62             output:
63             [
64             ["a","b","c"]
65             ]
66            
67             ===
68             input:
69             [["a","b","c"]]
70              
71             output:
72             [
73             ["a"],
74             ["b"],
75             ["c"]
76             ]
77              
78             ===
79             input:
80             [["a","b"], [1,2]];
81              
82             output:
83             [
84             [a, 1],
85             [a, 2],
86             [b, 1],
87             [b, 2]
88             ]
89              
90             ===
91             input:
92             ["a", "b", ["x","y"], {key=>"val"}]
93              
94             output:
95             [
96             ["a","b","x", {key=>"val"}],
97             ["a","b","y", {key=>"val"}]
98             ]
99              
100             Hash examples:
101              
102             ===
103             input:
104             {k1=>"a",k2=>"b",k3=>"c"}
105              
106             outputs:
107             [
108             {k1=>"a",k2=>"b",k3=>"c"}
109             ]
110            
111             ===
112             input:
113             {k1=>["a","b","c"]}
114              
115             output:
116             [
117             {k1=>"a"},
118             {k2=>"b"},
119             {k3=>"c"}
120             ]
121              
122             ===
123             input:
124             {k1=>["a","b"], k2=>[1,2]}
125              
126             output:
127             [
128             {k1=>"a", k2=>1},
129             {k1=>"b", k2=>2},
130             {k1=>"a", k2=>1},
131             {k1=>"b", k2=>2}
132             ]
133              
134             ===
135             input:
136             [
137             {k1=>"a", k2=>"b", k3=>["x","y"], k4=>{key=>"val"}}
138             ]
139              
140             output:
141             [
142             {k1=>"a",k2=>"b",k3=>"x", k4=>{key=>"val"}},
143             {k1=>"a",k2=>"b",k3=>"y", k4=>{key=>"val"}}
144             ]
145              
146              
147             =head1 API
148              
149             The module currently has a single function, which isn't exported. To use it it
150             must be addressed by its full name
151              
152             =head2 combinations
153              
154             my $result=Data::Combinations::combinations $ref;
155              
156             Generates the combinations of 'fields' in C<$ref>. A 'field' is either a hash
157             element or array element which contains a reference to an array. If a field
158             contains another scalar type, it is wrapped into an array of a single element.
159              
160             If C<$ref> is a hash, the keys are preserved in the outputs, with the values
161             for each key used for combination.
162              
163             If C<$ref> is an array, the indexes are preserved in the outputs, with the
164             values for each index used for combination.
165              
166             Return value is a reference to an array of the created combinations.
167              
168              
169             =head1 SEE ALSO
170              
171             There are other permutation modules. But they only work with flat lists?
172              
173             L
174              
175             L
176              
177             L
178              
179             =head1 AUTHOR
180              
181             Ruben Westerberg, Edrclaw@mac.comE
182              
183             =head1 REPOSITORTY and BUGS
184              
185             Please report any bugs via git hub: L
186              
187             =head1 COPYRIGHT AND LICENSE
188              
189             Copyright (C) 2022 by Ruben Westerberg
190              
191             This library is free software; you can redistribute it
192             and/or modify it under the same terms as Perl or the MIT
193             license.
194              
195             =head1 DISCLAIMER OF WARRANTIES
196              
197             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
198             OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
199             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
200             PARTICULAR PURPOSE.
201              
202              
203             =cut
204              
205 1     1   67903 use strict;
  1         3  
  1         30  
206 1     1   5 use warnings;
  1         1  
  1         41  
207              
208             our $VERSION = '0.1.0';
209              
210 1     1   6 use Carp qw;
  1         1  
  1         571  
211              
212              
213             #Internal subs
214             #==============
215             sub hash_combo {
216 1     1 0 3 my ($mixer, $list)=@_;
217 1         4 my @counter; #The current value of the column
218             my @keys; #The field name key for the column
219 1         0 my @vals; #array of arrays of value for the keys at the same index
220              
221 1         4 for (keys %$mixer){
222 3 100       11 if(ref($mixer->{$_}) eq "ARRAY"){
223 2         4 push @vals, $mixer->{$_}; #Push values directly
224             }
225             else{
226              
227 1         4 push @vals, [$mixer->{$_}]; #Make an array and then push
228             }
229 3         5 push @counter, 0; #setup the counter at 0
230 3         6 push @keys, $_; #remember the key
231             }
232              
233 1         2 my @output; #Return
234 1         3 my $carry=0; #Counter carry
235 1         4 until($carry){
236 9         13 $counter[0]++; #Tick the counter at lsb
237              
238             #Process carry
239 9         16 for my $c_index(0..$#counter){
240 21         27 $counter[$c_index]+=$carry; #Add the carry
241 21         28 $carry=0; #reset carry
242              
243 21 100       40 if($counter[$c_index] >= $vals[$c_index]->@*){
244 13         19 $counter[$c_index]=0;
245 13         15 $carry=1;
246             }
247 21 100       38 last unless $carry; #exit loop when no carry
248             }
249              
250             #Generate a new hash with the combination fields
251 9         11 my %hash;
252 9         16 for my $c_index(0..$#counter){
253 27         50 $hash{$keys[$c_index]}=$vals[$c_index][$counter[$c_index]];
254             }
255 9         21 push @output, \%hash;
256             }
257 1         5 \@output;
258             }
259              
260             sub array_combo {
261 1     1 0 3 my ($mixer, $list)=@_;
262 1         4 my @counter; #The current value of the column
263             my @keys; #The field name key for the column
264 1         0 my @vals; #array of arrays of value for the keys at the same index
265              
266 1         6 for (0..$mixer->@*-1){
267 3 100       9 if(ref($mixer->[$_]) eq "ARRAY"){
268 2         4 push @vals, $mixer->[$_]; #Push values directly
269             }
270             else{
271              
272 1         16 push @vals, [$mixer->[$_]]; #Make an array and then push
273             }
274 3         16 push @counter, 0; #setup the counter at 0
275 3         6 push @keys, $_; #remember the key
276             }
277              
278 1         2 my @output; #Return
279 1         3 my $carry=0; #Counter carry
280 1         3 until($carry){
281 9         15 $counter[0]++; #Tick the counter at lsb
282              
283             #Process carry
284 9         14 for my $c_index(0..$#counter){
285 13         20 $counter[$c_index]+=$carry; #Add the carry
286 13         18 $carry=0; #reset carry
287              
288 13 100       24 if($counter[$c_index] >= $vals[$c_index]->@*){
289 5         7 $counter[$c_index]=0;
290 5         7 $carry=1;
291             }
292 13 100       22 last unless $carry; #exit loop when no carry
293             }
294              
295             #Generate a new hash with the combination fields
296 9         13 my @array;
297 9         16 for my $c_index(0..$#counter){
298 27         45 $array[$keys[$c_index]]=$vals[$c_index][$counter[$c_index]];
299             }
300 9         20 push @output, \@array;
301             }
302 1         5 \@output;
303             }
304              
305             #Public subs
306             sub combinations {
307 2     2 1 1533 my $ref=ref($_[0]);
308 2 100       11 if($ref eq "HASH"){
    50          
309 1         4 &hash_combo;
310             }
311             elsif($ref eq "ARRAY"){
312 1         4 &array_combo;
313             }
314             else {
315 0           carp "Only hash and array reference is allowed";
316             }
317             }
318             1;
319             __END__