Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generate combinations from hash values

Tags:

hash

perl

In perl if I have a hash

my %ranges = (                                                                                      
'--tic' => [ 0, 1, 2 ],                                                                             
'--threads' => [ 8, 16 ],                                                                           
'--level' => [ 10, 20 ]                                                                               
);                                                                     

how can I generate an array of all the combinations, like

--level 10 --threads 8 --tic 0                                                                        
--level 10 --threads 8 --tic 1                                                                        
--level 10 --threads 8 --tic 2                                                                        
--level 10 --threads 16 --tic 0                                                                       
--level 10 --threads 16 --tic 1                                                                       
--level 10 --threads 16 --tic 2                                                                       
--level 20 --threads 8 --tic 0                                                                        
--level 20 --threads 8 --tic 1                                                                        
--level 20 --threads 8 --tic 2                                                                        
--level 20 --threads 16 --tic 0                                                                       
--level 20 --threads 16 --tic 1                                                                       
--level 20 --threads 16 --tic 2       

There can be any number of hash entries, and each entry can have any number of elements in it's value array. The order of the output array doesn't matter, just needs to have 1 element for each combination, 3*2*2 = 12 in this case, but could be any number.

I think some combination of splice, map, and foreach should work but I am flailing about in a bad way to find it.

like image 980
int Avatar asked Apr 15 '16 05:04

int


2 Answers

As already mentioned, you're looking for Cartesian product,

use strict;
use warnings;

sub getCartesian {
#
  my @input = @_;
  my @ret = map [$_], @{ shift @input };

  for my $a2 (@input) {
    @ret = map {
      my $v = $_;
      map [@$v, $_], @$a2;
    }
    @ret;
  }
  return @ret;
}

my %ranges = (
    '--tic' => [ 0, 1, 2 ],
    '--threads' => [ 8, 16 ],
    '--level' => [ 10, 20 ]
);

my @arr =  map {
  my $k = $_;
  [ map "$k $_", @{$ranges{$k}} ];
}
keys %ranges;

print "@$_\n" for getCartesian(@arr);

output

--level 10 --tic 0 --threads 8
--level 10 --tic 0 --threads 16
--level 10 --tic 1 --threads 8
--level 10 --tic 1 --threads 16
--level 10 --tic 2 --threads 8
--level 10 --tic 2 --threads 16
--level 20 --tic 0 --threads 8
--level 20 --tic 0 --threads 16
--level 20 --tic 1 --threads 8
--level 20 --tic 1 --threads 16
--level 20 --tic 2 --threads 8
--level 20 --tic 2 --threads 16
like image 171
mpapec Avatar answered Oct 13 '22 11:10

mpapec


The Set::Product module will do this for you

Here's an example program

use strict;
use warnings 'all';

use Set::Product 'product';

my %ranges = (
    '--tic'     => [ 0, 1, 2 ],
    '--threads' => [ 8, 16 ],
    '--level'   => [ 10, 20 ],
);

my @keys = sort keys %ranges;

product {
    print join(' ', map { "$keys[$_] $_[$_]" } 0 .. $#keys), "\n";
} @ranges{@keys};

output

--level 10 --threads 8 --tic 0
--level 10 --threads 8 --tic 1
--level 10 --threads 8 --tic 2
--level 10 --threads 16 --tic 0
--level 10 --threads 16 --tic 1
--level 10 --threads 16 --tic 2
--level 20 --threads 8 --tic 0
--level 20 --threads 8 --tic 1
--level 20 --threads 8 --tic 2
--level 20 --threads 16 --tic 0
--level 20 --threads 16 --tic 1
--level 20 --threads 16 --tic 2
like image 45
Borodin Avatar answered Oct 13 '22 11:10

Borodin