bradcathey has asked for the wisdom of the Perl Monks concerning the following question:
Fellow Monasterians,
As a web developer I spend lots of time with HTML::Template and it's requirement for AoH in it's <tmpl_loop ...> function, which I use frequently. Many of the lists I loop in the browser need to be grouped first. It's great when I can use GROUP BY when querying the database, but often I don't have that luxury. My question is, what's the best way to group a list?
My example has several entries for a few users that I want to group, including sums of hours worked and a monetary extension.
my $allusers = [ { 'user' => 'Sarah', 'duration' => 2, 'amount' => 200 }, { 'user' => 'William', 'duration' => 1, 'amount' => 100 }, { 'user' => 'Michael', 'duration' => 3, 'amount' => 300 }, { 'user' => 'Michael', 'duration' => 3, 'amount' => 300 }, { 'user' => 'William', 'duration' => 7, 'amount' => 700 }, { 'user' => 'Sarah', 'duration' => 5, 'amount' => 500 } ];
My first attempt sorts first and then iterates over the list—not especially efficient and a little goofy with the -1 counter:
$allusers = [ sort { $a->{'user'} cmp $b->{'user'} } @$allusers ]; my ($temp, $grouped); my $ctr = -1; for ( @{ $allusers } ) { if ( $temp && $temp eq $_->{'user'} ) { $grouped->[$ctr]{'amount'} += $_->{'amount'}; $grouped->[$ctr]{'duration'} += $_->{'duration'}; } else { $ctr++; $temp = $grouped->[$ctr]{'user'} = $_->{'user'}; $grouped->[$ctr]{'amount'} = $_->{'amount'}; $grouped->[$ctr]{'duration'} = $_->{'duration'}; } }
I reworked it to eliminate the sort and cut the processing time basically in half:
my $allkeys; for ( @{ $allusers } ) { my $user = $_->{'user'}; if ( !exists $allkeys->{$user} ) { my $attributes = { 'amount' => $_->{'amount'}, 'duration' => $_->{'duration'} }; $allkeys->{$user} = $attributes; } else { my $refAttributes = $allkeys->{$user}; $refAttributes->{'amount'} += $_->{'amount'}; $refAttributes->{'duration'} += $_->{'duration'}; } } my $finalArray; while ((my $key, my $value) = each %{$allkeys}) { my $hash = { 'user' => $key, 'amount' => $value->{'amount'}, 'duration' => $value->{'duration'} }; push @{$finalArray}, $hash; }
OUTPUT for both methods are the same:
print Dumper ($finalArray); $VAR1 = [ { 'amount' => 600, 'user' => 'Michael', 'duration' => 6 }, { 'amount' => 800, 'user' => 'William', 'duration' => 8 }, { 'amount' => 700, 'user' => 'Sarah', 'duration' => 7 } ];
But even the second way seems a bit tedious with that second while loop to build the final array. Is there a better way?
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Ways to group elements of an AoH
by kyle (Abbot) on Jan 04, 2009 at 03:01 UTC | |
by bradcathey (Prior) on Jan 04, 2009 at 04:41 UTC | |
|
Re: Ways to group elements of an AoH
by GrandFather (Saint) on Jan 04, 2009 at 03:05 UTC | |
|
Re: Ways to group elements of an AoH
by fmerges (Chaplain) on Jan 04, 2009 at 21:40 UTC | |
by bradcathey (Prior) on Jan 05, 2009 at 01:03 UTC | |
by fmerges (Chaplain) on Jan 05, 2009 at 02:32 UTC |