in reply to Sorting an array of hashes by a value of hash

When defining your own sort-function, you have access to two special variables, $a and $b, which contain the values to be sorted. Using them, your solution could look like
for my $taskname (sort { $tasks->{$a}{priority} <=> $tasks->{$b}{prior +ity} } keys %$tasks )
Rata

Replies are listed 'Best First'.
Re^2: Sorting an array of hashes by a value of hash
by simonozzy (Initiate) on Apr 13, 2011 at 13:21 UTC

    Thanks Rata, that works perfectly. I looked at the FAQ, but the special variables confused me, so your explanation helped.

    If some of the tasks have no specified priority, I get an "uninitialized" error, but it still works, assuming low priority if it's missing (that was after I switched $a and $b)

      You may be making a mistake if there are undefs in your hash. That warning is telling you something. If you don't mind that undef and zero (0) get sorted as the same value, you have nothing to worry about with rata's solution, and the warnings can be ignored. But if undef should be ordered differently from zero, you need to do an additional check.

      Please have a look at the following test code that demonstrates three ways to handle your sort, while also dealing with the warnings.

      use 5.012_002; use strict; use warnings; my %hash = ( this => 1, that => 4, the => undef, other => undef, those => 2, it => 0, ); # First simply disable the warning temporarily. { no warnings qw/uninitialized/; my @warnsorted = sort { $hash{$a} <=> $hash{$b} } keys %hash; say "Without Warnings:\t@warnsorted"; } # Second, check each item for definedness. my @defsorted = sort { ( defined( $hash{$a} ) && $hash{$a} ) <=> ( defined( $hash{$b} ) && $hash{$b} ) } keys %hash; say "Testing Definedness:\t@defsorted"; # Third, sort with definedness as a criteria my @critsorted = sort { defined( $hash{$a} ) <=> defined( $hash{$b} ) or ( defined( $hash{$a} ) && $hash{$a} ) <=> ( defined( $hash{$b} ) && $hash{$b} ) } keys %hash; say "Defined as Criteria:\t@critsorted";

      And the output:

      Without Warnings: the it other this those that Testing Definedness: the it other this those that Defined as Criteria: the other it this those that

      The first method shown simply disables the warning, lexically scoped to a very narrow scope surrounding the sort. But this doesn't deal with the fact that 'undef' and '0' will get sorted together as the same effective value.

      The second method eliminates the warning by not directly sorting undefs. What it does is test each side of the comparison for definedness. If defined, then compare the value on that side of the <=>. If undefined, compare the value returned by defined(), which would be zero in that case. This preserves the possibly errant artifact of undef being comparatively the same as zero.

      The third method first does a definedness comparison on both sides of the <=>. If those are equal, then it moves on to a regular sort similar to my second example. This method will promote undef to the top of the heap, above '0'. Of course you could demote it to the bottom of the pile just as easily. But the point is that it sorts undef as a value different from zero.

      There's a final solution which reduces the number of calls to defined, by combining the first method with the third:

      { no warnings qw/uninitialized/; my @sorted = sort { defined( $hash{$a} ) <=> defined( $hash{$b} ) or $hash{$a} <=> $hash{$b} } keys %hash; say "Defined as Criteria:\t@sorted"; }

      Here we are still sorting with undef as a distinctly different value from zero, but warnings would continue to be generated. Since we're already sure that we've dealt with that warning, it would be safe to just ignore it, which is what the no warnings...... does for us.


      Dave

        Wow, thanks for the detailed explantion, Dave. I sort of understand what you've done and managed to get it (option 3) to work in my code. I also got it to put the undefined ones with the low priority (high number) jobs by swapping the a and b in the first cmp, which is exactly what I needed. Who needs experts-exchange.com when you have wise monks!