# How to sort a hash of hashes by value?

Before attempting to sort a hash of hashes by values, one should make sure to be familiar with the question: How to sort a hash in Perl?

## The problem

Given the following reference to a hash of hashes, how can we sort them based on the value of **Position**?

my $data = { 'Gaur 3' => { 'Max' => '85', 'Type' => 'text', 'Position' => '10', 'IsAdditional' => 'Y', 'Required' => 'Y', 'Mandatory' => 'Y', 'Min' => '40' }, 'Gaur 2' => { 'Max' => '90', 'Type' => 'text', 'Position' => '11', 'IsAdditional' => 'Y', 'Required' => 'Y', 'Mandatory' => 'Y', 'Min' => '60' }, 'Gaur 1' => { 'Max' => '80', 'Type' => 'text', 'Position' => '10', 'IsAdditional' => 'Y', 'Required' => 'Y', 'Mandatory' => 'Y', 'Min' => '40' }, };

Of course, we cannot really sort the hash, but we can sort the keys. As $data is a reference to a hash, first we need to de-reference it by putting a % in front of it. calling keys on this construct will return the list of keys in a random order.

my @keys = keys %$data; foreach my $k (@keys) { say $k; }

Result might look like this, but it can be in a different order on other runs:

Gaur 1 Gaur 3 Gaur 2

$a and $b are the standard place-holders of sort. For
each comparison they will hold two keys from the list.
In order to compare the **Position** value of two elements we need to access them
using $a and $b and compare the numerical(!) values using the
spaceship-operator (<=>).

my @positioned = sort { $data->{$a}{Position} <=> $data->{$b}{Position} } keys %$data; foreach my $k (@positioned) { say $k; }

The result is:

Gaur 3 Gaur 1 Gaur 2

This would be nice, but unfortunately this result is not going to be the same always.
After all the **Position** values of 'Gaur 1' and 'Gaur 3' are both the same number (10),
so the space-ship operator cannot really decide between the two.

## Secondary sorting

If we would like to make sure the result of sort is more predictable, we will need to sort based on two (or even more) values.

For example we can sort the hash first by the **Position** value, and among the entries with the
same Position value we can sort by the value of the **Max** field.
In order to do this we will use the following expression:

my @maxed = sort { $data->{$a}{Position} <=> $data->{$b}{Position} or $data->{$a}{Max} <=> $data->{$b}{Max} } keys %$data; foreach my $k (@maxed) { say $k; }

And the result is:

Gaur 1 Gaur 3 Gaur 2

Of course if, we encounter two entries where both the **Position** and the **Max** fields have the exact same value,
then even this sort won't provide the same results on every call.

Published on 2014-09-11