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

Dear Masters,
Given the HoHoA below, and list of selected stores "@st", I want to sort the selected stores based on its earning (first element of the AoA)
my %bighash = ( 'set1' => { 'STORE1' => [10,['1','BOOK'],['2','PENCIL']], 'STORE2' => [8,['0','CHALK'],['4','PEN']], }, 'set2' => { 'STORE3' => [13,['1','BOOK'],['2','PENCIL']], 'STORE4' => [11,['0','CHALK'],['4','PEN']], }, 'set3' => { 'STORE5' => [2, ['1','BOOK'],['2','PENCIL']], 'STORE6' => [70,['0','CHALK'],['4','PEN']], }, 'set4' => { 'STORE7' => [19,['1','BOOK'],['2','PENCIL']], 'STORE8' => [20,['0','CHALK'],['4','PEN']], } ); # list of selected stores (st) my @st = ('STORE2','STORE3','STORE5'); foreach ( @st ) { foreach my $grp ( sort {$bighash{$b}{$_}[0] <=> $bighash{$a}{$_}[0]} keys %bighash ) { if ( defined $bighash{$grp}{$_}) { print "STORE: $_ EARNING:$bighash{$grp}{$_}[0]\n"; foreach my $j ( 1 .. (@{$bighash{$grp}{$_}})-1 ) { print join(",",@{$bighash{$grp}{$_}[$j]}),"\n"; } print "\n"; } } }
But, why my code above fail to produce the sorted selected stores like this?
STORE: STORE3 EARNING:13 1,BOOK 2,PENCIL STORE: STORE2 EARNING:8 0,CHALK 4,PEN STORE: STORE5 EARNING:2 1,BOOK 2,PENCIL
What's wrong with my "sorting syntax" above?

---
neversaint and everlastingly indebted.......

Replies are listed 'Best First'.
Re: Sorting by Hash Slice
by tlm (Prior) on Jul 28, 2005 at 04:16 UTC

    One problem I see is that you are apparently trying to sort the keys of %bighash, even though their ordering is not relevant to your task. Besides, you are doing this for each of the desired stores, when what you should be doing is sorting the stores according to the data you have for them (i.e. not iterating over them).

    Here's a not-particularly-clever-but-serviceable way to do it:

    # first get rid of the nuisance "set" keys (i.e. the primary keys of % +bighash); # they add nothing to the solution and just get in the way. my %flatten; for my $h ( values %bighash ) { @flatten{ keys %$h } = values %$h; } # now pull out the desired data from %flatten, pack it in records # whose first element is the name of the store, and sort these records # in ascending numerical order according to the [ 1 ][ 0 ]-th element # of each record (which corresponds to the earnings). my @sorted_data = sort { $b->[ 1 ][ 0 ] <=> $a->[ 1 ][ 0 ] } map [ $_, $flatten{ $_ } ], @st; # now it's just a matter of printing the sorted records. for my $record ( @sorted_data ) { print "STORE: $record->[ 0 ] EARNING:$record->[ 1 ][ 0 ]\n"; for my $i ( 1 .. $#{ $record->[ 1 ] } ) { print join( ',', @{ $record->[ 1 ][ $i ] } ), "\n"; } print "\n"; }
    This is a pretty naive solution that makes no attempt at optimization. E.g. it may be unacceptably wasteful to flatten %bighash just to more easily access a few records.

    On the other hand, the problem doesn't look well-posed to me, so a more efficient solution at this stage may be premature. What I mean by not being well-posed is that it is not clear that, in general, records corresponding to the same store won't appear in different sets.

    the lowliest monk

Re: Sorting by Hash Slice
by sacked (Hermit) on Jul 28, 2005 at 04:57 UTC
    This solution is similar to tim's but doesn't use a second intermediate data structure. It uses the flattened hash (from the first step) inline.
    # selected stores my @st= ('STORE2','STORE3','STORE5'); # list of stores (doesn't keep duplicates) my %stores= map { %$_ } values %bighash; + foreach my $store ( sort { $stores{$b}[0] <=> $stores{$a}[0] } @st ) { my $products= $stores{$store}; my $earned = shift @$products; + print "STORE: $store EARNING: $earned\n"; foreach my $product ( @$products ) { print join( "," => @$product ), "\n"; } print "\n"; }
    As tim says, without a more detailed problem description, it seems that the extra level of nesting in %bighash (set1, set2, etc.) is unnecessary.

    --sacked