Thanks to everyone for all the responses. Set::CrossProduct does indeed do exactly what I wanted, except that the real problem I'm working with wasn't using straight arrays, and it seemed complicated to get Set::CrossProduct to do what I wanted with the inputs I have.

So, in case you're interested, here's the solution I came up with in the end. I've just included one element of the DATA hash at the end; there are lots like that. Any comments or suggestions you have would be welcomed.

local $\ = "\n"; local $/ = ''; open CODES, "> $code_file" or die "Couldn't open codefile: $!\n"; my $csv = Text::CSV->new; $csv->combine('code', 'desc'); print CODES $csv->string; my %hash = eval <DATA>; my @full_code_list = (); foreach my $key (keys %hash) { print "Looking at code [$key]...\n"; my @list = gen_codes($hash{$key}); push @full_code_list, @list if @list; } foreach my $aref (@full_code_list) { my ($code, $desc) = @$aref; $csv->combine($code, $desc); print CODES $csv->string; } sub gen_codes { my $href = shift; my $source = $href->{'source'}; my $base_desc = $href->{'desc'}; my @codes = (); my @element_hashes = @$href{@{$href->{'elements'}}}; my $code_str_sub = $href->{'code_str'}; my $desc_str_sub = $href->{'desc_str'}; my @code_args = ($source); my @desc_args = ($base_desc); sub getvalues { my ($elems, $codes, $c_sub, $d_sub, $code_args, $desc_args) = +@_; my ($element, @remaining_elems) = @$elems; for my $code (keys %$element) { my $desc = $element->{$code}; my $c_args = [ @$code_args, $code ]; my $d_args = [ @$desc_args, $desc ]; if (@remaining_elems) { getvalues(\@remaining_elems, $codes, $c_sub, $d_sub, $ +c_args, $d_args); } else { push @$codes, [&$c_sub(@$c_args), &$d_sub(@$d_args)]; } } } getvalues(\@element_hashes, \@codes, $code_str_sub, $desc_str_sub, + \@code_args, \@desc_args); return @codes; } __END__ my %hash = ( 'FI_SWP_PLAIN_VANILLA-SPREADS' => { source => 'FDER,SWAPSPRD', desc => 'Swaps - Plain Vanilla Spreads', elements => [qw/term attr/], term => { '2Y' => '2 Years', '3Y' => '3 Years', '4Y' => '4 Years', '5Y' => '5 Years', '6Y' => '6 Years', '7Y' => '7 Years', '8Y' => '8 Years', '9Y' => '9 Years', '10Y' => '10 Years', '12Y' => '12 Years', '15Y' => '15 Years', '20Y' => '20 Years', '25Y' => '25 Years', '30Y' => '30 Years', '40Y' => '40 Years' }, attr => { RT_MID => 'Mid Rate', RT_BID => 'Bid Rate', RT_OFF => 'Offer Rate', AM_MOD_DUR => 'Modified Duration', DT_VALUE => 'Value Date', DT_MATURITY => 'Maturity Date' }, code_str => sub { my ($s,$t,$a) = @_; return "DB($s,$t,$a)"; }, desc_str => sub { my ($d,$t,$a) = @_; return "$d: $t $a"; } } );

In reply to Re: generating combinations of elements of an arbitrary number of arrays by mickey
in thread generating combinations of elements of an arbitrary number of arrays by mickey

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.