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

Dear monks,

I have a hash of hashes of hashes. The deepest hash has a key 'RANK', with values R#1, R#2, R#3 etc, depending on the key in the middle hash:
$entry{$table}{A}{RANK} = R#3; $entry{$table}{C}{RANK} = R#2; $entry{$table}{B}{RANK} = R#1;
and now I'd like to populate an array with the keys of the middle hash, numerically sorted by the integers in R#1, R#2, R#3 etc, i.e.:
@array = qw(B C A);
by sorting the %entry hash, somehow. But how? Many thanks for any hints! (Note that the A, B, C keys are just for example...)

Replies are listed 'Best First'.
Re: sort question
by dragonchild (Archbishop) on May 17, 2005 at 18:16 UTC
    my @array = sort { $entry{$table}{$a}{RANK} cmp $entry{$table}{$b}{RANK} } keys %{$entry{$table}};

    Explanation of the code is left as an exercise for the reader.


    • In general, if you think something isn't in Perl, try it out, because it usually is. :-)
    • "What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?"
      or much faster...
      use Sort::Key qw(nkeysort); my @array = nkeysort { $entry{$table}{$_}{RANK} } keys %{$entry{$table}};
Re: sort question
by mda2 (Hermit) on May 17, 2005 at 18:32 UTC
    It's a work for Schwartzian Transform...
    $entry{$table}{A}{RANK} = R#3; $entry{$table}{C}{RANK} = R#2; $entry{$table}{B}{RANK} = R#1; @array = map { $_->[0] } sort { $b->[1] cmp $a->[1] } map { [ $_, $entry{$table}{$_}{RANK} ] } keys %{$entry{$table}}; print ">> @array <<\n"; __END__ >> A C B <<
    Update:
  • Change sort from <=> to cmp.
  • Dragonchild did one more single and efficient code!

    --
    Marco Antonio
    Rio-PM

      Hey mda2, I tweaked it a bit and it works, but I have NO idea why or how:
      sub sort_ranks { my @array = map {$_->[0]} sort {($a->[1]=~/^R#(\d+)$/)[0] <=> ($b->[1] +=~/^R#(\d+)$/)[0]} map {[$_,$entry{$table}{$_}{RANK}]} @_; }
      In fact, I think I'll use this as my JAPH ;-)
        if you don't know how it works, i really hope you're not using it for anything important! otherwise you'll be in trouble when you try to maintain it in a couple months! "now why did i do it like this... well, turns out i didn't even know then, and i'm more clueless now!"

        this is what is called a Schwartzian Transform, so check out 441762 and http://www.sysarch.com/perl/sort_paper.html
Re: sort question
by mrborisguy (Hermit) on May 17, 2005 at 18:19 UTC
    if you already have a list of the keys of the middle hash (A,B,C), then you can do this:
    my @listofkeys = ('A','B','C'); @sortedlistofkeys = sort { $entry{$table}{$a}{RANK} <=> $entry{$table{ +$b}{RANK} } @listofkeys;
    Update:
    Got beaten to the punch, and dragonchild had a better answer, too! ack!