journey has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,
The following snippet populates a HoA from 2 arrays by:
  1. incrementing a counter for "classes" in the first position of the array
  2. cumulating respective "quantities" in the second position.
I got it working with the use of tmp variables and individual assignation statements for each class/qty.
I would be very grateful for any suggestions so as to make it more elegant/shorter/smarter. (It shouldn't be too hard ;-)
Many thanks.
use strict; use warnings; use Data::Dumper; my @class = (40, 40, 40, 183, 183, 259, 244, 503); my @qty = (260, 234, 211, 301, 401, 321, 210, 451); my %bin = ( 0 => [0,0], 40 => [0, 0], 183 => [0, 0], 503 => [0, 0], 442 => [0, 0], 259 => [0, 0], 244 => [0, 0]); my ($i, $hkey); for($i = 0; $i <= $#class; $i++) { $hkey = $class[$i]; my $tmp_count = $bin{$hkey}[0]+1; my $tmp_sum = $bin{$hkey}[1]+ $qty[$i]; $bin{$hkey}[0] = $tmp_count; $bin{$hkey}[1] = $tmp_sum; # $bin{$hkey}[0, 1] = [$tmp_count, $tmp_sum]; #why not? } print Dumper (%bin);
Expected results:
# 0 => [ 0, 0]; # 40 => [ 3, 705]; # 183 => [ 2, 702]; # 259 => [ 1, 401]; # 442 => [ 0, 0]; # 244 => [ 1, 321]; # 503 => [ 1, 210];

Replies are listed 'Best First'.
Re: Better assignment to Hash of Arrays
by GrandFather (Saint) on Oct 22, 2009 at 01:52 UTC

    I'd:

    use strict; use warnings; use Data::Dump::Streamer; my @class = (40, 40, 40, 183, 183, 259, 244, 503); my @qty = (260, 234, 211, 301, 401, 321, 210, 451); my %bin = map {$_ => [0, 0]} (0, 40, 183, 503, 442, 259, 244); for my $i (0 .. $#class) { my $hkey = $class[$i]; @{$bin{$hkey}} = ($bin{$hkey}[0]+1, $bin{$hkey}[1]+ $qty[$i]); } Dump (\%bin);

    Note the use of a Perl for loop in place of the C loop and an array assignment in the for loop. Oh, and keep the scope of variables as small as possible ($hkey is declared inside the loop).


    True laziness is hard work
      I think I might be inclined to an oH instead of an oA:
      >perl -wMstrict -le "my @class = ( 40, 40, 40, 183, 183, 259, 244, 503); my @qty = (260, 234, 211, 301, 401, 321, 210, 451); my %bin = map { $_ => { class => 0, qty => 0 } } 0, 40, 183, 503, 442, 259, 244 ; for my $i (0 .. $#class) { $bin{ $class[$i] }{class}++; $bin{ $class[$i] }{qty} += $qty[$i]; } for my $key (sort { $a <=> $b } keys %bin) { printf qq{%4d => [%2d, %4d] \n}, $key, @{ $bin{$key} }{ qw(class qty) }; } " 0 => [ 0, 0] 40 => [ 3, 705] 183 => [ 2, 702] 244 => [ 1, 210] 259 => [ 1, 321] 442 => [ 0, 0] 503 => [ 1, 451]
      Exactly what I was looking for! Nice and Perlish... Thanks for taking the time to provide me with valuable tips. Regards
Re: Better assignment to Hash of Arrays
by CountZero (Bishop) on Oct 22, 2009 at 06:15 UTC
    Another way of doing it, using List::MoreUtils:
    use strict; use List::MoreUtils qw/mesh natatime/; use Data::Dumper; my @class = ( 40, 40, 40, 183, 183, 259, 244, 503 ); my @qty = ( 260, 234, 211, 301, 401, 321, 210, 451 ); my %bin = ( 0 => [ 0, 0 ], 40 => [ 0, 0 ], 183 => [ 0, 0 ], 503 => [ 0, 0 ], 442 => [ 0, 0 ], 259 => [ 0, 0 ], 244 => [ 0, 0 ], ); my $it = natatime 2, mesh @class, @qty; while ( my ( $class, $quantity ) = $it->() ) { ( $bin{$class}->[0] )++; $bin{$class}->[1] += $quantity; } print Dumper( \%bin );

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James