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

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. :)

Replies are listed 'Best First'.
Re: Re: Re: Re: Sorting values of nested hash refs
by Limbic~Region (Chancellor) on Mar 01, 2004 at 14:40 UTC
    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.