Perl: Sort Hash Values by Key with a Hash Slice

In Perl, hash (associative array) sorting is a common and easy practice. Sorting values by key is easy. And so is sorting by value. But how do you sort the values of a hash by key? One answer is to use a hash slice.

Sorting Hashes

As an example, take a simple hash showing some people and their ages:

my $people = { Paul   => 59,
               Sheila => 59,
               Anne   => 56,
               John   => 54,
               James  => 49,
             };

Keys Sorted by Key

Getting a sorted list of keys is easy.

print sort keys $people;

…produces the output:

Anne James John Paul Sheila

Values Sorted by Value

Now print a list of values sorted by value. The “” operator is used to make it a numerical sort:

print sort { a  $b } values $people;

As expected, we get a sorted list of ages:

49 54 56 59 59

Keys Sorted by Value

This one is very common. Extract the keys, then use an embedded sort that refers to values:

print sort { $people->{$a}  $people->{$b} } keys $people;

As expected, it outputs a list of names sorted by age[1]:

James John Anne Sheila Paul

Values Sorted by Key

This one is not needed as often as the others. But to do it, use a hash slice:

my @k = sort keys $people;
print @{$people}{@k}

And we get our values (ages in this example) sorted by key (people’s names). A list of ages sorted by the alphanumeric order of their owners’ names. So 56 is first (Anne), followed by 40 (James), 54 (John) and so on.

56 49 54 59 59

The @k is a sorted array of keys, which is used as an index back in to the hash. A hash slice uses an array (or list) as an index, and produces another list containing the corresponding values.

Putting it in one line:

print @{$people}{ sort keys $people };

Further explanation

The slice in this case contains all values of the original array. So it is more of an entire “loaf” than a slice. It might be useful where the key you are using for an associative array also has some relevance to the data or to the kind of reporting you want to do. For example, a sequence number that reflects a specific data order.


Notes

Note 1 – when sorting keys by value, if two values are the same, the key order is indeterminate, and will often differ across subsequent runs of the same code. For example, the perl command ”sort { $people->{$a} $people->{$b} } keys $people” sometimes prints “James John Anne Sheila Paul” and at other times, “James John Anne Paul Sheila”, due to Paul and Sheila having the same ages. This will often be important, giving false positives, for example, if the output is checksummed for testing. The usual remedy is to add another level of sorting to ensure the output is repeatable. For example:

print sort { $people->{$a}  $people->{$b} || $a cmp $b } keys $people;

The extra “|| $a cmp $b” will additionally sort by people’s names (with cmp) if the ages are equal.

Note 2 – In the above article, formatting has been removed for clarity. The output is shown space separated without their being any code (such as join) to perform the separation.

Leave a Reply

Your email address will not be published. Required fields are marked *