Reputation: 19047
I have a list in Perl, and I'm trying to "expand" any lists it contains.
For example:
my @l = (
'foo',
['A', 'B'],
'bar',
['X', 'Y', 'Z'],
'baz',
);
I'd like to expand the lists to get this:
my @want = (
[qw/ foo A bar X baz /],
[qw/ foo A bar Y baz /],
[qw/ foo A bar Z baz /],
[qw/ foo B bar X baz /],
[qw/ foo B bar Y baz /],
[qw/ foo B bar Z baz /],
);
I came up with this, which works, but is ugly and doesn't result in sensibly ordered array:
my @out = ([]);
foreach my $elt (@l){
if( ref($elt) eq 'ARRAY' ){
my $n = scalar(@$elt);
my @_out;
# expand
for( my $i=0; $i<$n; $i++ ){
foreach my $o (@out){
push(@_out, [@$o]);
}
}
for( my $i=0; $i<@_out; $i++ ){
push(@{$_out[$i]}, $elt->[$i % $n]);
}
@out = @_out;
}
else{
foreach my $o (@out){
push(@$o, $elt);
}
}
}
Is there a more concise way to accomplish this operation?
Upvotes: 0
Views: 94
Reputation: 6613
Although this is an old thread, I thought I'd add another possible solution
The module Set::CrossProduct will also get the result wanted. (The input array elements must all be array references.)
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dump;
use Set::CrossProduct;
my @l = (
'foo',
['A', 'B'],
'bar',
['X', 'Y', 'Z'],
'baz',
);
# all elements of array must be array refs
@l = map ref eq 'ARRAY' ? $_ : [$_], @l;
my @wanted = Set::CrossProduct->new(\@l)->combinations;
dd \@wanted;
The data dump output was
[
["foo", "A", "bar", "X", "baz"],
["foo", "A", "bar", "Y", "baz"],
["foo", "A", "bar", "Z", "baz"],
["foo", "B", "bar", "X", "baz"],
["foo", "B", "bar", "Y", "baz"],
["foo", "B", "bar", "Z", "baz"],
]
Upvotes: 0
Reputation: 61540
It looks like you are trying to compute the Cartesian product of the elements in your array. You can use Math::Cartesian::Product
from CPAN to do this:
use strict;
use warnings;
use Math::Cartesian::Product;
cartesian { print "@_","\n"; }
([qw(foo)], [qw(A B)], [qw(bar)], [qw(X Y Z)], [qw(baz)]);
# outputs
# foo A bar X baz
# foo A bar Y baz
# foo A bar Z baz
# foo B bar X baz
# foo B bar Y baz
# foo B bar Z baz
Note, this does require you to place each of your input elements in an arrayref and if you want an array of arrayrefs as your output (from your example) you will need to encode that logic in the block.
Upvotes: 2