Question

Can anybody tell me what I am doing wrong here? I have tried just about every possible combination of array / hash type and sort query I can think of and cannot seem to get this to work.

I am trying to sort the hash ref below by value1 :

my $test = {
    '1' => { 'value1' => '0.001000', 'value2' => 'red'},
    '2' => { 'value1' => '0.005000', 'value2' => 'blue'},
    '3' => { 'value1' => '0.002000', 'value2' => 'green'},
    '7' => { 'value1' => '0.002243', 'value2' => 'violet'},
    '9' => { 'value1' => '0.001005', 'value2' => 'yellow'},
    '20' => { 'value1' => '0.0010200', 'value2' => 'purple'}
};

Using this sort loop:

foreach (sort { $test{$a}->{'value1'} <=> $test{$b}->{'value1'} } keys \%{$test} ){
    print "key: $_ value: $test->{$_}->{'value1'}\n"
}

I get:

key: 1 value: 0.001000
key: 3 value: 0.002000
key: 7 value: 0.002243
key: 9 value: 0.001005
key: 2 value: 0.005000
key: 20 value: 0.0010200

I have tried with integers and the same thing seems to happen.

I don't actually need to loop through the hash either I just want it ordered for later use. Its easy to do with an array of hashes, but not so with a hash of hashes..?

Was it helpful?

Solution

Don't call keys on a reference. Call it on the actual hash.

Also, this $test{$a}->, should be $test->{$a}, because $test is a hash reference.

foreach (sort { $test->{$a}{'value1'} <=> $test->{$b}{'value1'} } keys %{$test} ){
    print "key: $_ value: $test->{$_}->{'value1'}\n"
}

If you had use strict; and use warnings; turned on, you would've gotten the following error to alert you to an issue:

Global symbol "%test" requires explicit package name

OTHER TIPS

Just wanted to provide a source for the other answers, and a working code example. Like they said, you are calling keys with a hash reference for the argument. According to the documentation:


Starting with Perl 5.14, keys can take a scalar EXPR, which must contain a reference to an unblessed hash or array. The argument will be dereferenced automatically. This aspect of keys is considered highly experimental. The exact behaviour may change in a future version of Perl.

for (keys $hashref) { ... }
for (keys $obj->get_arrayref) { ... }

However this does work for me:

#!/usr/bin/perl
use strict;
use warnings;

my $test = {
    '1' => { 'value1' => '0.001000', 'value2' => 'red'},
    '2' => { 'value1' => '0.005000', 'value2' => 'blue'},
    '3' => { 'value1' => '0.002000', 'value2' => 'green'},
    '7' => { 'value1' => '0.002243', 'value2' => 'violet'},
    '9' => { 'value1' => '0.001005', 'value2' => 'yellow'},
    '20' => { 'value1' => '0.0010200', 'value2' => 'purple'}
};


foreach (sort { $test->{$a}->{'value1'} <=> $test->{$b}->{'value1'} } keys \%{$test} ) {
    print "key: $_ value: $test->{$_}->{'value1'}\n"
}

Example:

matt@mattpc:~/Documents/test/10$ perl test.pl 
key: 1 value: 0.001000
key: 9 value: 0.001005
key: 20 value: 0.0010200
key: 3 value: 0.002000
key: 7 value: 0.002243
key: 2 value: 0.005000
matt@mattpc:~/Documents/test/10$ perl --version
This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi
(with 88 registered patches, see perl -V for more detail)

This is with using a hash reference as the input to keys which I would not recommend.

I'd recommend following the advice of the other questions and adding use strict and use warnings and changing the hash reference to a hash, %{test}.

It's simply keys %$test. The argument of keys must be a hash, not a hashref. \%${test} is the same as $test, a ref.

And use $test->{$a}, not $test{$a}, as $test is a hash-ref, not a hash.

foreach (sort { $test->{$a}->{'value1'} <=> $test->{$b}->'{value1'} } keys %$test) {
    print "key: $_ value: $test->{$_}->{'value1'}\n"
}

or a shorter form with some syntactic sugar: You can omit the additional arrows after the first one. And you don't have to quote string literal keys when addressing hashes.

foreach (sort { $test->{$a}{value1} <=> $test->{$b}{value1} } keys %$test) {
    print "key: $_ value: $test->{$_}{value1}\n"
}

It usually helps a lot to turn on use warnings;, at least for debugging.

The only wrong thing I can spot is usage of hash ref \%{$test} where you should use hash %$test. keys work with that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top