This is something I'm writing using the Template Toolkit in Perl, but it's more a generic algorithm problem. My basic problem is that given a data structure like this:
my @array = (
[qw /00 01/],
[qw /10/],
[qw /20 21/],
[qw /30 31 32 33 /],
);
I need output like this (simplified for illustration):
<00>
<10>
<20> <30>(00/10/20/30)</30> <31>(00/10/20/31)</31>
<32>(00/10/20/32)</32> <33>(00/10/20/30)</33>
</20>
<21> <30>(00/10/21/30)</30> <31>(00/10/21/31)</31>
<32>(00/10/21/31)</32> <33>(00/10/21/31)</33>
</21>
</10>
</00>
<01>
<10>
<20> <30>(01/10/20/30)</30> <31>(01/10/20/31)</31>
<32>(01/10/20/32)</32> <33>(01/10/20/33)</33>
</20>
<21> <30>(01/10/21/30)</30> <31>(01/10/21/31)</31>
<32>(01/10/21/32)</32> <33>(01/10/21/33)</33>
</21>
</10>
</01>
This being a simplified example of the nested html tables that are the real output. The path at the central nodes are actually the arguments to be called to another subroutine to populate the nested tables with data. I'm fairly certain that a transpose of the original array structure will be useful, so I wrote Array::Transpose::Ragged and released it on CPAN earlier today.
I managed an implementation which builds the nested structure from the inside to the outside (using perl's Template Toolkit - see below), but by the time I get to the outer parts of the structure I no longer have the opportunity to populate the required data at the central nodes. Here's that implementation for what it's worth:
[% SET inner = "(path data should go here)" %]
[% MACRO process_groups(line, inner) BLOCK %]
[% FOREACH l IN line %]
<[% l %]>[% inner %]</[% l %]>
[% END %]
[% END %]
[% WHILE (x = records.pop) %]
[% inner = process_groups(x, inner) %]
[% END %]
[% inner %]
Any suggestions for the approach I should be taking to get this right
UPDATE:
For interest, I thought I'd put the TT version of the accepted answer up. A little tricky because TT isnt quite as flexible as perl, but here goes:
#!/usr/bin/env perl
use warnings;
use strict;
use Template;
my $template = Template->new();
my @array = (
[ qw/00 01/ ], [ qw/10/ ],[ qw/20 21/ ], [ qw/30 31 32 33/ ]);
my $stash = { records => \@array, };
$template->process(\*DATA, $stash) || die $template->error(), "\n";
__END__
[% MACRO print_output(data, path_elements) BLOCK; %]
[% current = data.0; remaining = data.slice(1); %]
[% FOREACH d IN current %]
<[% d %]>
[% IF remaining.size > 0 %]
[% path_elements.push(d); print_output(remaining, path_elements); %]
[% SET discard = path_elements.pop %]
[% ELSE %]
([% path_elements.join('/') _ '/' _ d %])
[% END %]
</[% d %]>
[% END %]
[% END %]
[% SET path = []; print_output(records, path) %]
And even better here's the actual nested table structure in TT:
[% MACRO print_output(data, path_elements) BLOCK; %]
<table> <tr>
[% current = data.0; remaining = data.slice(1); %]
[% FOREACH d IN current %]
<th>[% d %]</th>
[% END %] </tr>
<tr>
[% FOREACH d IN current %]
[% IF remaining.size > 0 %]
<td id="[% d %]">[% path_elements.push(d); print_output(remaining, path_elements); %]</td>
[% SET discard = path_elements.pop %]
[% ELSE %]
<td>([% path_elements.join('/') _ '/' _ d %])</td>
[% END %]
[% END %]
</tr></table>
[% END %]
[% SET path = []; print_output(records, path) %]
Not sure I understand the full context within which you are working, but here's a stab at the general problem:
use strict;
use warnings;
my @array = (
[ qw/00 01/ ],
[ qw/10/ ],
[ qw/20 21/ ],
[ qw/30 31 32 33/ ],
);
print_output(\@array);
sub print_output {
my ($data, @path_elements) = @_;
my $level = @path_elements;
my ($current, @remaining) = @$data;
for my $d (@$current){
print ' ' x $level, "<$d>\n";
if (@remaining){
print_output(\@remaining, @path_elements, $d);
}
else {
print ' ' x ($level + 1), "(", join('/', @path_elements, $d), ")\n";
}
print ' ' x $level, "</$d>\n";
}
}