No such thing as a small change PerlMonks

### Twice the pleasure of sorting a hash

by coldfingertips (Pilgrim)
 on Apr 26, 2004 at 09:41 UTC Need Help??
coldfingertips has asked for the wisdom of the Perl Monks concerning the following question:

I have a hash I need sorted..TWICE. I asked this in the chat box but that got confusing, so here's the problem.

I need to sort a hash by values (the values are all whole numbers) but in some cases, many of the hash keys have the same value. How can I then sort these alphabetically?

Unsorted

```rock   => 3
candle => 25
bug    => 3
rain   => 12
dust   => 17
spider => 12
Using foreach (sort {\$saved_key{\$b} cmp \$saved_key{\$a}} keys %saved_key)
```candle => 25
dust   => 17
spider => 12
rain   => 12
rock   => 3
bug    => 3
Desired:
```candle => 25
dust   => 17
rain   => 12
spider => 12
bug    => 3
rock   => 3
If two or more values are the same, I want to sort them alphanumerically. Any help would be much appreciated.

Replies are listed 'Best First'.
Re: Twice the pleasure of sorting a hash
by broquaint (Abbot) on Apr 26, 2004 at 09:52 UTC
A double comparison should do it e.g
```my %h = (
rock   => 3,
candle => 25,
bug    => 3,
rain   => 12,
dust   => 17,
spider => 12,
);

for( sort { \$h{\$b} <=> \$h{\$a} || \$a cmp \$b } keys %h ) {
print "\$_ => \$h{\$_}\n";
}

__output__

candle => 25
dust => 17
rain => 12
spider => 12
bug => 3
rock => 3
So firstly we sort by value in descending order then optionally sort by key value.
HTH

_________
broquaint

That works perfectly, you're the best!! Thanks so much for your help broquaint!!
Re: Twice the pleasure of sorting a hash
by Limbic~Region (Chancellor) on Apr 26, 2004 at 12:14 UTC
coldfingertips,
It seems that people often want to use a hash so they can have key lookup functionality but also want the hash to come out in a specific order as well.

If you want the hash to come out in the order it was created, than Tie::IxHash is probably for you ( and if you need speed look at Tie::Hash::Indexed which is written in XS ). Do not use these modules if you need your hash sorted though. While it does provide 3 methods to resort the hash (user provided key list or ASCIIBetically by keys/values), it does not provide any mechanism for retaining auto-sorting new keys/values.

This is one of the reasons I wrote Tie::Hash::Sorted. It allows the user to define their own sort routine (even using lexicals the module wouldn't normally see) as well as numerous optimization options to choose from. Your code would look something like:
```#!/usr/bin/perl
use strict;
use warnings;
use Tie::Hash::Sorted;

my \$sort = sub {
my \$hash = shift;
[ sort { \$hash->{\$b} <=> \$hash->{\$a} || \$a cmp \$b } keys %\$hash ];
};
tie my %sorted_data, 'Tie::Hash::Sorted', 'Sort_Routine' => \$sort;
%sorted_data = (
rock => 3, candle => 25, bug => 3,
rain => 12, dust => 17, spider => 12
);
print "\$_ : \$sorted_data{\$_}\n" for keys %sorted_data;

Modifying the hash does not require any additional work to keep the sorted order.

Cheers - L~R

Re: Twice the pleasure of sorting a hash
by ph0enix (Friar) on Apr 26, 2004 at 09:57 UTC

Use following code for sorting

sort {\$tmp = \$saved_key{\$b} <=> \$saved_key{\$a}; \$tmp = \$a cmp \$b if (\$tmp == 0); \$tmp} keys %saved_key

Temporary variable is not needed of course

While your code works, it's one of those occasions where you're making code less readable by being more explicit.
```...
sort {  \$h{\$b} <=> \$h{\$a}  ||  \$a cmp \$b  }
keys %saved_key;
Unlike C or Java, the Perl operator || keeps the actual true value. (C or Java would just return 1; but they don't have <=> either.) This means (among other things) that Perl can cascade a number of possible criteria in a single statement. By avoiding the visual clutter of temporary variables and unnecessary checks against zero, your code can be more readable.

This is the Perl idiom. List each criteria from most important to least important. The first criteria which returns a -1 or 1 will decide the sort order; any criteria returning 0 will fall through to the next part of the expression.

```my @sorted =
sort {          \$hash{\$b}  <=>  \$hash{\$a}          ||
\$a  cmp  \$b                 ||
func(\$a)  <=>  func(\$b)           ||
\$other{name}{\$b}  cmp  \$other{name}{\$a}    }
@unsorted;
I find that a newline after each criteria helps the reader immediately understand what's going on. (The extra spacing on each line is optional.)

This newline on each phase technique is useful of any of the Perl-style "list pipelines" which are common, such as those with colorful names like the Schwartzian Transform or the Orcish Maneuver.

--
[ e d @ h a l l e y . c c ]

Re: Twice the pleasure of sorting a hash
by Anonymous Monk on Apr 26, 2004 at 19:00 UTC
I used: sort {\$saved_key{\$b} <=> \$saved_key{\$a} || \$a cmp \$b} The first term returns zero if the values are the same (note the numeric compare, the string compare does not provide the sort show in the example). This in turn triggers the text compare on the keys. Worked for me.
Re: Twice the pleasure of sorting a hash
by Anonymous Monk on Apr 26, 2004 at 20:19 UTC
try this:
```
foreach (sort { (\$saved_key{\$b} <=> \$saved_key{\$a}) || (\$a cmp \$b) } keys %saved_key)

```
That sorts the hash by values (numerically), and then by keys (alphabetically)

Create A New User
Node Status?
node history
Node Type: perlquestion [id://348119]
Approved by mpolo
Front-paged by matija
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (10)
As of 2017-08-18 15:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Who is your favorite scientist and why?

Results (303 votes). Check out past polls.

Notices?