waghso
waghso

Reputation: 595

How can I generate permutations from this array?

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

Answers (2)

Borodin
Borodin

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;
}

output

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

Zaid
Zaid

Reputation: 37136

You could perform dynamic recursion or reach out to a CPAN module, but I'd use glob in this case:

my @out = glob join '', map "{$_}", @arr1;

Upvotes: 7

Related Questions