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

Hi guys, I was wondering how I could go about sorting a hash that is grown in the following manner:
my $Rec = { ORG_LEVEL_DESCRIP=>$rec->[10], GRADE=>strip_hyphen($rec->[15]), POSITION=>$rec->[2], NAME=>invert_name($rec->[16]), AGE=>convert_date_to_age(trim($rec->[17])), DATE_OF_BIRTH=>date_of_birth_cleansed($rec->[17 +]), COUNTRY=>$rec->[19], }; push @{$AG{$rec->[10]}},$Rec;
I have tried the following:
foreach my $p ( sort { $$b{GRADE} <=> $$a{GRADE}} @{$AG}) { ................. }

I get the the following output: D1 P5 P2 P3

Instead, I would like to output by letter first and then by number as illustrated below: D1 P5 P5 P4 P4 P3 P3 P2 P1 etc.

In other words, I would like to sort by this value GRADE=>strip_hyphen($rec->15) where the P5, P4, etc. are stored.
SAMPLE DATA P5 , Health Officer, M. Famzo, France, 1/1/1957 P5 , Health Officer,S. Fey, United States, 4/16/1960 P2 , Health Officer, F. Ban, France, 10/5/1966 P4 , Health Officer, J. Pino, Germany, 9/19/1948 P3 , Health Officer,G. Brown, Greece, 5/15/1974 D1 , Health Officer, J. Hill, Syria, 2/10/1962
Any ideas.

Replies are listed 'Best First'.
Re: Sorting a Hash
by Eily (Monsignor) on Sep 03, 2014 at 15:03 UTC

    You're lucky if you actually get any kind ofsorting, because <=> is the numeric comparison operator, this means that perl will expect number, and turn "D1", "P5" and your other strings (except those that start by a number) into the value 0. It's quite easy to remember which operator do what in perl, numeric operators look like mathematics symbols (== < > <= >= <=>) and string operators look like strings (eq lt gt le ge cmp).

    Now, as long as your numbers are one digit long, all you need to do is sort { $b->{GRADE} cmp $b->{GRADE} } @$AG; where cmp is the alphabetic comparison operator.

    But if you can have numbers that are longer you'll have to choose between to behaviours. Either you still want alphabetic order, and then the code I gave you will work, and P10 will be sorted before P2 (because 1 is before 2, and in alphabetic order you look at one char at a time and ignore the rest), or you want to sort by using the full number. In the latter case, the way to provide several sort conditions is:

    sort { $a->{NAME} cmp $b->{NAME} # compare name (will return 0 if they a +re the same), using alphabetic order (cmp) or $a->{AGE} <=> $b->{AGE} # compare age only if the names are equ +al, use numeric order (<=>) } @people;
    This means that you'll have to find a way to extract the letter and number from your string (not that I couldn't give you the answer, but you'll understand it better if you find it by yourself).

Re: Sorting a Hash
by LanX (Saint) on Sep 03, 2014 at 13:52 UTC
    > how I could go about sorting a hash

    you don't seem to sort hashes but hash-refs by entries.

    hashes can't be sorted, only their keys, (you'll find myriads of examples in the archives)

    > I get the the following output: D1 P5 P2 P3

    there are no D1 P5 P2 P3 in your sample data.

    In order to understand you better please provide clear code, especially a (little) Data::Dumper dump of your data structure, instead of a C&P of your source.

    see also How (not) to ask a question

    • Only Post Relevant Code
    • Include Sample Data (I/O)

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

    UPDATE

    OP updated his node, w/o indication

Re: Sorting a Hash
by GuiPerl (Acolyte) on Sep 04, 2014 at 09:35 UTC
    The solution was in using the substr function together with the cmp operator to get the intended output.
    foreach my $p ( sort { substr($$a{GRADE},0,1) cmp substr($$b{GRADE}, +0,1) || substr($$b{GRADE},0,2) cmp substr($$a{GRADE} +,0,2)} @{$AG{$A}}) { output D2 D1 P5 P5 P4 P3 P3 P2 P1 etc..... .......... }