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

I know, this has been covered hundreds of times before, and I have read all the examples I could find, but still cannot figure out why I can't get this bit of code to work. Trying to reverse-sort numerically by value2 THEN alpha by key. It's got to be something simple i'm missing.
my %HoH = ( "a" => { "value1" => "foo", "value2" => "1"}, "e" => { "value1" => "bar", "value2" => "2"}, "b" => { "value1" => "foo", "value2" => "2"}, "f" => { "value1" => "bar", "value2" => "2"}, "d" => { "value1" => "foo", "value2" => "3"}, "c" => { "value1" => "bar", "value2" => "5"}, "h" => { "value1" => "foo", "value2" => "4"}, "g" => { "value1" => "foo", "value2" => "4"} ); foreach my $key (sort { $HoH{$key}->{"value2"}{$b} <=> $HoH{$key}->{"v +alue2"}{$a} || $a cmp $b } keys %HoH ) { my $v1 = $HoH{$key}{"value1"}; my $v2 = $HoH{$key}{"value2"}; printf "%s has v1=%s and v2=%d\n", $key, $v1, $v2; }
PS: i'm just learning so go easy on me.

Replies are listed 'Best First'.
Re: double sort HoH by value/key
by johngg (Canon) on Nov 21, 2007 at 22:02 UTC
    You are very close. Change the first term of the sort to

    $HoH{$b}->{"value2"} <=> $HoH{$a}->{"value2"}

    I would do without the complication of printf as you are not doing any fancy formatting.

    use strict; use warnings; my %HoH = ( "a" => { "value1" => "foo", "value2" => "1"}, "e" => { "value1" => "bar", "value2" => "2"}, "b" => { "value1" => "foo", "value2" => "2"}, "f" => { "value1" => "bar", "value2" => "2"}, "d" => { "value1" => "foo", "value2" => "3"}, "c" => { "value1" => "bar", "value2" => "5"}, "h" => { "value1" => "foo", "value2" => "4"}, "g" => { "value1" => "foo", "value2" => "4"} ); print map {qq{$_ has v1=$HoH{$_}->{value1} and v2=$HoH{$_}->{value2}\n}} sort { $HoH{$b}->{value2} <=> $HoH{$a}->{value2} || $a cmp $b } keys %HoH;

    I hope this is of use.

    Cheers,

    JohnGG

      Thanks, that was it. I guess I still don't understand why, but at least I have a working example to go off of. The hash of hashes concept is a little mind boggling at first. (the only reason I was using the printf was because abstracted out this example from some other code I was working on). Much appreciated.
        I guess I still don't understand why ...

        Perhaps this explanation will help. You passed each key of %HoH into your sort routine (where they are accessed as $a and $b) in order to get those keys back out in a particular order. Your second term, the keys ascending alphabetically caused you no problem. The first term, the value keyed by 'value2' in the sub-hash numerically descending, required a little more work. You needed to bear in mind that since the keys of %HoH were passed into to sort, when accessing %HoH inside the routine, you needed to use $a and $b in exactly the same place you would normally use a key into %HoH.

        If you find that your mind has become somewhat boggled by data structures, especially ones that you have built programmatically, I recommend that you use Data::Dumper to examine them. It gives a nice human-readable representation of the structure which is, usefully, valid Perl code.

        I hope this helps your understanding but if you still have questions please ask further.

        Cheers,

        JohnGG

Re: double sort HoH by value/key
by mwah (Hermit) on Nov 21, 2007 at 22:13 UTC

    You have been close. Johngg did already post one solution (he was faster than me ;-)

    Another variant would be:

    use strict; use warnings; my %HoH = ( a => { value1 => "foo", value2 => 1 }, e => { value1 => "bar", value2 => 2 }, b => { value1 => "foo", value2 => 2 }, f => { value1 => "bar", value2 => 2 }, d => { value1 => "foo", value2 => 3 }, c => { value1 => "bar", value2 => 5 }, h => { value1 => "foo", value2 => 4 }, g => { value1 => "foo", value2 => 4 } ); my @sorted = sort HoHsort keys %HoH; sub HoHsort { $HoH{$b}{value2} <=> $HoH{$a}{value2} || $a cmp $b } print map "$_ =>=> { $HoH{$_}{value2} } \n", @sorted;

    Regards

    mwa

      I think my trouble is in understanding what $a and $b are referring to. I find it hard to comprehend how sort can "see outside" of the foreach loop. I think I get it now, though. I am going to try to read up more on sort.
        I think my trouble is in understanding what $a and $b are referring to.

        They are referring to the elements you pass into the sort routine. Which element of that list gets associated when to $a or to $b is sort's business. doc;//sort assigns elements of that list to $a and $b as aliases, as the underlying sort algorithm demands.

        You were passing keys %HoH to the sort function, hence the keys of the outer hash, the chars (a .. g) in the order perl's hashing implementation sees fit. $a and $b, inside the sort function block, will never be anything else than an alias to an element of the list you passed in.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

        I find it hard to comprehend how sort can "see outside" of the foreach loop.

        That's common for all blocks.

        my $var = "Hi!"; { print("$var\n"); }
        my $var = "Hi!"; for (1..2) { print("$var\n"); }
        my $var = "Hi!"; print(map { "$var\n" } 1..2);
        my $var = "Hi!"; sub func { print("$var\n"); } func();

        Yes, the following works even though $var goes out of scope before func is called.

        { my $var = "Hi!"; sub func { print("$var\n"); } } func();