Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Sorting a hash by 3 different keys

by Ineffectual (Scribe)
on Feb 24, 2004 at 01:06 UTC ( [id://331275]=perlquestion: print w/replies, xml ) Need Help??

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

So, I have a hash of a hash. Each hash of a hash has 10 entries and the hash has around 12k. I'd like to sort the hash by 3 of the inner hash keys and print it.

Example:

$hash{$key1}{'ITEM1'} = '3';
$hash{$key1}{'ITEM2'} = '3';
$hash{$key1}{'ITEM3'} = '3';
$hash{$key2}{'ITEM1'} = '2';
$hash{$key2}{'ITEM2'} = '2';
$hash{$key2}{'ITEM3'} = '2';
$hash{$key3}{'ITEM1'} = '1';
$hash{$key3}{'ITEM2'} = '1';
$hash{$key3}{'ITEM3'} = '1';

I'd like to sort the hash so that it comes out key3, key 2, key1 based on ITEM1, ITEM2, ITEM3. All ITEMS are always numbers ranging from 1 - 32.
Thanks.
Ineff

Replies are listed 'Best First'.
Re: Sorting a hash by 3 different keys
by jweed (Chaplain) on Feb 24, 2004 at 01:16 UTC
    So you want sorting based on comparison of ITEM1, 2, and 3 in that order? Try:
    @sorted = sort { $hash{$a}{ITEM1} <=> $hash{$b}{ITEM1} || $hash{$a}{ITEM2} <=> $hash{$b}{ITEM2} || $hash{$a}{ITEM3} <=> $hash{$b}{ITEM3} } keys %hash;



    Code is (almost) always untested.
    http://www.justicepoetic.net/
Re: Sorting a hash by 3 different keys
by davido (Cardinal) on Feb 24, 2004 at 04:25 UTC
    jweed provided an accurate code example. It relies on a combination of two great little tricks.

    The first is the logical short circuit "or".

    The way 'or' works ('||' also) is to evaluate the first argument first. If it evaluates to truth, there's no need to go further. Truth, in a Perl sense, is anything that isn't false or undef. If the first argument (the left hand side of the 'or' operator) evaluates to false, then the right hand side must be evaluated for truth or falseness. Think of it as "try this, and if it doesn't work, try that".

    The logical short circuit "or" operator can be chained; this, or that, or some other thing, or yet another..., and so on. Always, once truth is found (as far to the left-hand-side of the chain as possible), the remainder of the chain is not evaluated.

    That leads to the next neat little trick.

    The comparison operator (<=>, and cmp) returns -1 or 1 for less-than and greater-than comparison results, but returns zero for equality. Zero is false, as far as Perl is concerned. So what happens if you chain something together like this?...

    $a <=> $b or $c <=> $d or $e <=> $f

    Just read it through: Compare $a and $b. If they're not equal, return a value of -1 or 1. But if they're equal, then the first comparison returns false, and the 'or' short-circuit operator realizes it must evaluate the right-hand side to find a non-false result. $c and $d are compared. If they're different, a -1 or 1 is returned. If they're equal, zero is returned and the next 'or' forces evaluation of the next expression.

    With this idiom you can chain any number of complex comparisons together in a sort routine. It's really kind of nifty. Actually, I was tinkering with this idiom about three weeks ago in the context of dynamically-specified sort criteria, and it eventually led me to this meditation (which you may find demonstrative of the techniques involved in complex sorting): Fun with complex sorting on arbitrary criteria list.

    Enjoy!


    Dave

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://331275]
Approved by diotalevi
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2024-03-29 00:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found