Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort a hash of hashes by key in Perl?

Tags:

sorting

hash

perl

I want to sort a hash which actually has a hash as a value. For instance:

my %hash1=(
   field1=>"",
   field2=>"",
   count=>0,
);
my %hash2;
$hash2{"asd"}={%hash1};

and I inserted lots of hashes to %hash2 with different count values of %hash2.

How can I sort the %hash1 according to a count value of hash1?

Is there a way of doing this without implementing quicksort manually, for instance with the sort function of Perl?

like image 587
systemsfault Avatar asked Apr 13 '09 06:04

systemsfault


2 Answers

my @hash1s = sort {$a->{count} <=> $b->{count}} values %hash2;
like image 154
Chris Jester-Young Avatar answered Sep 24 '22 01:09

Chris Jester-Young


From perlfaq4, the answer to "http://faq.perl.org/perlfaq4.html#How_do_I_sort_a_hash" has most of the information you need to put together your code.

You might also want to see the chapter on Sorting in Learning Perl.

Chris has a completely fine answer, although I hate using values like that. A more familiar way to do the same thing is to go through the keys of the top-level hash but sort by the second-level key:

my @sorted_hashes = 
    sort { $hash2->{$a}{count} <=> $hash2->{$b}{count} } 
    keys %hash2;

I do it this way because it's a little less mind-bending.


How do I sort a hash (optionally by value instead of key)?

(contributed by brian d foy)

To sort a hash, start with the keys. In this example, we give the list of keys to the sort function which then compares them ASCIIbetically (which might be affected by your locale settings). The output list has the keys in ASCIIbetical order. Once we have the keys, we can go through them to create a report which lists the keys in ASCIIbetical order.

my @keys = sort { $a cmp $b } keys %hash;

foreach my $key ( @keys )
    {
    printf "%-20s %6d\n", $key, $hash{$key};
    }

We could get more fancy in the sort() block though. Instead of comparing the keys, we can compute a value with them and use that value as the comparison.

For instance, to make our report order case-insensitive, we use the \L sequence in a double-quoted string to make everything lowercase. The sort() block then compares the lowercased values to determine in which order to put the keys.

my @keys = sort { "\L$a" cmp "\L$b" } keys %hash;

Note: if the computation is expensive or the hash has many elements, you may want to look at the Schwartzian Transform to cache the computation results.

If we want to sort by the hash value instead, we use the hash key to look it up. We still get out a list of keys, but this time they are ordered by their value.

my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash;

From there we can get more complex. If the hash values are the same, we can provide a secondary sort on the hash key.

my @keys = sort {
    $hash{$a} <=> $hash{$b}
        or
    "\L$a" cmp "\L$b"
    } keys %hash;
like image 21
brian d foy Avatar answered Sep 21 '22 01:09

brian d foy