in reply to Re: Sorting values of nested hash refs
in thread Sorting values of nested hash refs

neniro,
I do not believe this accomplishes what doran was after. Instead of finding the 3 highest values of the inner hashes, you are finding the highest value in each inner hash. I think the way to go is to sort all the second level hash values into an array and pick the top 3.
my @vals = sort {$b <=> $a } map { values %{$hoh->{$_}} } keys %$hoh; my ($one, $two, $three) = @vals[0..2];
Cheers - L~R

Update Completely missed that tinita had an almost identical solution below. In the case of a very large HoH, you can avoid sorting and gain a bit of efficiency with the following:

my @top_3; my $filled = 0; VAL: for my $val ( map { values %{$hoh->{$_}} } keys %$hoh ) { if ( ! $filled ) { for ( 0 .. 2 ) { if ( ! defined $top_3[$_] ) { $top_3[$_] = $val; $filled = 1 if $_ == 2; next VAL; } } } if ( $val > $top_3[0] ) { ($top_3[0], $top_3[1]) = ($val, $top_3[0]); } elsif ( $val > $top_3[1] ) { ($top_3[1], $top_3[2]) = ($val, $top_3[1]); } elsif ( $val > $top_3[2] ) { $top_3[2] = $val; } } print "$_\n" for @top_3;

Replies are listed 'Best First'.
Re: Re: Re: Sorting values of nested hash refs
by doran (Deacon) on Mar 01, 2004 at 07:06 UTC
    Well (d'oh), an important thing I forgot to mention is that I also need to know where I am in the $HoH for any given value. So I not only need to find the top x (10 in my case) values of $HoH->{$foo}{$bar}, but I also need to keep track of the value of $foo and $bar for each of those x values.

    Anyway, your code gave me some idea and this is what I've come up with for now.
    while (my $kid_ = $kills_sth->fetchrow_hashref()){ $KP->{$kid_->{k}}{$kid_->{v}}++; } # How many do we keep? my $total = 10; my %top; for(keys %{$KP}){ # Player ID my $pid = $_; VAL: for ( keys %{$KP->{$pid}}) { # Target ID my $tid = $_; my $frags = $KP->{$pid}{$tid}; for(0 .. ($total - 1)){ my $idx = $_; $top{$idx}{f} ||= 0; if(($idx != ($total - 1)) && $frags > $top{$idx}{f}){ my $idx2 = $idx + 1; $top{$idx2}{k} = $top{$idx}{k}; $top{$idx2}{v} = $top{$idx}{v}; $top{$idx2}{f} = $top{$idx}{f}; $top{$idx}{k} = $pid; $top{$idx}{v} = $tid; $top{$idx}{f} = $frags; next VAL; } elsif(($idx == ($total - 1)) && ($frags > $top{$idx}{f})){ $top{$idx}{k} = $pid; $top{$idx}{v} = $tid; $top{$idx}{f} = $frags; next VAL; } } } } for(0 .. ($total - 1)){ my $id = $_; printf("%2s. p: %s\tt: %s\tf: %s\n",($id+1),$top{$id}{k},$top{$id} +{v},$top{$id}{f}); }
    Still, it seems awfully inefficient to me and I'm really leary of it's ninja scaling abilities. But that said, it works and produces the intended results:
    1. p: 2838413 t: 3440380 f: 328 2. p: 2838413 t: 1261188 f: 282 3. p: 539960 t: 2976790 f: 273 4. p: 3440380 t: 2838413 f: 268 5. p: 182560 t: 53957 f: 206 6. p: 539960 t: 53957 f: 196 7. p: 1261188 t: 53957 f: 190 8. p: 539960 t: 3440380 f: 171 9. p: 3440380 t: 539960 f: 146 10. p: 53957 t: 1261188 f: 127

    I'd appreciate any ideas about increasing efficiency.

    Thanks a bunch for your help.

    ps. Yes, it's a half-life stats program. :)
      doran,
      I think you might find the following a bit more efficient and a bit more readable:
      # An inline loop is more efficient then a block - thought almost imper +ceptable $KP->{ $_->{k} }{ $_->{v} }++ while $kills_sth->fetchrow_hashref(); # Before I did not know if it was safe to assume no negative numbers - + now I can my %top = map { $_ => {'score' => 0, 'pid' => '', 'tid' => ''} } 1 .. +10; my @items = qw(score pid tid); for my $pid ( keys %{ $KP } ) { SCORE: for my $tid ( keys %{ $KP->{$pid} } ) { my $score = $KP->{$pid}{$tid}; next if $score < $top{10}{score}; # May or may not improve +efficiency for my $rank ( 1 .. 10 ) { if ( $score > $top{$rank}{score} ) { (@{$top{$rank}}{@items}, @{$top{$rank + 1}}{@items}) = + ($score, $pid, $tid, @{$top{$rank}}{@items}); next SCORE; } } } } for my $rank ( 1 .. 10 ) { printf("%.2d. f: %s\tp: %s\tt: %s\n", $rank, @top{@items}); }
      This would be much more efficient if it could be done with SQL. You would have to provide more info to determine that.

      Cheers - L~R

      Update: 2004-03-17 This is broken. To fix it you would make %top an AoA, handle fence post issues, and splice entries in the middle while popping off the end. This version will randomly lose values. See this for a working example.