Reputation: 595
I have an array
my @arr1 = ("A1", "B1,B2", "C1,C2,C3", ..);
how to create new array from this by appending the elements in such a way that it will take one sub-element from @arr1 like A1, B1 & C1 to create a new element "A1B1C1". Sub-elements are separated by ",". The array element number is dynamic.
So the resulting array should look as
@out = qw/A1B1C1 A1B1C2 A1B1C3 A1B2C1 A1B2C2 A1B2C3/;
Upvotes: 2
Views: 101
Reputation: 126722
For some inscrutable reason I have an aversion to using glob
to generate cross-products like this. It goes along with my dislike of using say
in production code, perhaps because it feels like a debugging tool
I can think of nothing wrong with using glob
in this way, but I thought I should offer a module-based solution in addition to Zaid's fine answer
It's easy to assume that either Math::Combinatorics
or Algorithm::Combinatorics
will help us here. Together with Graph
, one of these modules is invaluable a component of a Perl programmer's toolbox, but they both work on only a single set of values and it is a cross-product of multiple sets that is required here
I have chosen Brian Foy's Set::CrossProduct
here
My benchmarks show that it is around 150 times faster than the glob
trick, but that shouldn't matter unless the calculation is on the critical path of an application that you are trying to optimise
use strict;
use warnings 'all';
use feature 'say';
use Set::CrossProduct;
my @arr1 = ('A1', 'B1,B2', 'C1,C2,C3');
my $iterator = Set::CrossProduct->new( [ map [ split /,/ ], @arr1 ] );
while ( my $tuple = $iterator->get ) {
say join '', @$tuple;
}
A1B1C1
A1B1C2
A1B1C3
A1B2C1
A1B2C2
A1B2C3
To use this module in a way plug-compatible with Zaid's answer, you would write
my @out = map { join '', @$_ } $iterator->combinations;
Upvotes: 4