3
votes

Hi I have a data structure which is of the following form:

$data = {                                                                                           
    'a' => { key1 => 2, key2 => 1 },                                                                   
    'b' => { key1 => 1, key2 => 2 },                                                                   
    'c' => { key1 => 1, key2 => 1 },                                                                   
    'd' => { key1 => 3, key2 => 2 },                                                                   
    'e' => { key1 => 3, key2 => 1 },                                                                   
    'f' => { key1 => 1, key2 => 2 },                                                                   
};

what I want to be able to do is loop through this data structure in ascending order of key2, descending order of key1, and then ascending order of the hash key, e.g:

e
a
c
d
b
f

how can I achieve this in perl? I know that I can sort the hash by key using sort keys %$data, but how can I sort by multiple values and keys?

1

1 Answers

10
votes

A similar question has been asked before and can be found here: Sorting an Array of Hash by multiple keys Perl

Basically, Perl has two operators for sorting, <=> and cmp which return -1, 0 or 1, depending on if the left hand side is less than, equal to, or greater than the right hand side. <=> is used for numerical comparison, and cmp is used for stringwise comparison. More detail about their use can be found here: Equality Operators.

These operators can be used with Perl's sort function and in conjuction with the or operator can be used to achieve the result you're after:

#!/usr/bin/perl

use strict;
use warnings;

my $data = {
    'a' => { key1 => 2, key2 => 1 },
    'b' => { key1 => 1, key2 => 2 },
    'c' => { key1 => 1, key2 => 1 },
    'd' => { key1 => 3, key2 => 2 },
    'e' => { key1 => 3, key2 => 1 },
    'f' => { key1 => 1, key2 => 2 },
};

my @sorted = sort {
        $data->{$a}->{key2} <=> $data->{$b}->{key2} or
        $data->{$b}->{key1} <=> $data->{$a}->{key1} or
        $a cmp $b
    } keys %{$data};

for my $key (@sorted){
    print "$key\n";
}

Since <=> and cmp return 0 (false) for equality, it means we can chain equality checks together with or or ||.

In the example above we $a and $b to refer to a key of the $data hashref at a particular iteration of the sort. Using these keys we can access particular values of the hash, e.g. $data->{$a}->{key2}. By putting $a before $b it will cause the sort to be in ascending order, and putting $b before $a will cause the sort to be in descending order.