Not to belittle your efforts in any way, but IMHO your solution is far too complicated and hard to comprehend, and I believe this is because it tries to do too much. IMHO, the grouping function should be separate from any processing/analysis of the groups. If you simply return the groups to the caller, she can do whatever processing on them in her own way.
Here is how I would probably approach the problem:
sub find_groups(&\@)
{
my( $keymap, $rows_ar ) = @_;
my %groups;
for ( @$rows_ar )
{
push @{ $groups{ &$keymap } }, $_;
}
\%groups
}
Example: Show a
count(*) of distinct values of the 'name' column:
my $groups_hr = find_groups { $_->{'name'} } @rows;
for my $k ( sort keys %$groups_hr )
{
print "$k\t" . @{$groups_hr->{$k}} . "\n";
}
Example: Group by a key consisting of multiple columns concatenated:
find_groups { join $;, @{$_}{qw( name game score )} } @rows;
Example: Group by a key derived from the row by an arbitrarily complex function:
find_groups {
$_->{'name'} eq 'foo' ? 1 :
$_->{'game'} =~ /bar/ ? 2 :
$_->{'score'} >= 500 ? 3 : 0
} @rows;
Example: Find numeric mean within each group of the values in the 'score' field:
use List::Util qw( sum );
my $groups_hr = find_groups { $_->{'name'} } @rows;
for my $k ( sort keys %$groups_hr )
{
my $avg = sum( map { $_->{'score'} } @{$groups_hr->{$k}} ) / @{$gr
+oups_hr->{$k}};
print "$k\t$avg\n";
}
Sure, you could argue that
map { $_->{'score'} } @rows
is an ugly way of extracting a column from a AoH table... but at least it's perlish.
You could easily wrap that in a function if you wanted to.
Between the mind which plans and the hands which build, there must be a mediator... and this mediator must be the heart.