Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: Access a hashref sorted by value

Tags:

hash

perl

I'm writing a script that'll read through my ftpd logs and generate a hash as follows:

$stats = \{
            'user1' => {
                        'files' => 281,
                        'size' => '3724251021'
                      },
            'user2' => {
                      'files' => 555,
                      'size' => '7385856997'
                    },
            'user3' => {
                          'files' => 235,
                          'size' => '3716904486'
                        },
            'user4' => {
                       'files' => 578,
                       'size' => '8536026929'
                     }
          };

How do I access this hash with the keys sorted by size?

I tried this but I get an error saying not a hashref

foreach my $user (sort { $$stats->{$a}->{size} cmp $$stats->{$b}->{size} } keys %$stats) {

blahblahblah...
}
like image 545
somebody Avatar asked Mar 04 '10 12:03

somebody


3 Answers

Your problem isn't related to sorting but to the unusual way you have defined $stats -- namely, as a reference to a hash reference.

my %hash            =  (a => 1, b => 2);
my $hash_ref        =  {c => 3, d => 4};
my $ref_to_hash_ref = \{e => 5, f => 6};

You can use either this:

my $stats = { ... };

foreach my $user ( 
        sort { $stats->{$a}{size} <=> $stats->{$b}{size} }
        keys %$stats ) {
    ...
}

Or this, if you really must do the ref-to-hash-ref thing:

my $stats = \{ ... };

foreach my $user (
        sort { $$stats->{$a}{size} <=> $$stats->{$b}{size} }
        keys %$$stats ) {
    ...
}

Note also that you probably want to use <=> (numeric comparison) rather than cmp (string comparison).

like image 187
FMc Avatar answered Oct 19 '22 14:10

FMc


You can pass a function to sort in order to get the values in the order you want.

#!/usr/bin/perl

use strict;

my $stats = {
            'user1' => {
                          'files' => 281,
                          'size'  => '3724251021'
                       },

            'user2' => {
                          'files' => 555,
                          'size'  => '7385856997'
                       },

            'user3' => {
                          'files' => 235,
                          'size'  => '3716904486'
                       },

            'user4' => {
                          'files' => 578,
                          'size'  => '8536026929'
                       }
          };

foreach my $key (sort sortBySize (keys(%$stats)))
{
     print $key, " => ", $stats->{$key}->{'files'}, " - ",
           $stats->{$key}->{'size'}, "\n";
}

exit;

sub sortBySize
{
    return $stats->{$b}->{'size'} <=> $stats->{$a}->{'size'};
}

OUTPUT:

user4 => 578 - 8536026929
user2 => 555 - 7385856997
user1 => 281 - 3724251021
user3 => 235 - 3716904486

If you want them sorted lowest to highest, you just need to switch the $b and $a in the sortBySize function:

return $stats->{$a}->{'size'} <=> $stats->{$b}->{'size'};
like image 5
RC. Avatar answered Oct 19 '22 13:10

RC.


Your first line seems to be mistaken. It should be

$stats = {

Your attempt suffers from another syntax error; you need only a single sigil when dereferencing with ->, and I believe it is better to refrain from bareword hashkeys:

foreach my $user (sort { $stats->{$a}->{'size'} cmp $stats->{$b}->{'size'} }
                       keys %$stats) {
  # ...
  }
like image 1
Svante Avatar answered Oct 19 '22 14:10

Svante