sortingperlperl-hash

How to sort perl hash?


I have the below hash structure, hash of hash.

$VAR1 = { '191' => { 'test1' => { 'score' => '9.18' }, 'test2' => { 'score' => '2.84' }, 
                     'test3' => { 'score' => '15.62' }, 'test4' => { 'score' => '11.84' }, 

          '190' => { 'test1'=> { 'score' => '13.28' }, 'test2' => { 'score' => '-47.56' }, 
                     'test3' => { 'score' => '18.50' }, 'test4' => { 'score' => '14.88' } } }

I am trying to sort the hash based on the 'score' value. The sorting should happen only among whats inside of the main keys '191' and '190' . See below hash structure for expected result.

$VAR1 = { '191' => {'test3' => { 'score' => '15.62' }, 'test4' => { 'score' => '11.84' }
                    'test1' => { 'score' => '9.18' }, 'test2' => { 'score' => '2.84' }, 
                     
        ​ '190' => { 'test3' => { 'score' => '18.50' }, 'test4' => { 'score' => '14.88' }
                    'test1'=> {'score' => '13.28' }, 'test2' => { 'score' => '-47.56' } } }

Sorting is to be done based on descending score value.

I have tried out the below code but it kind-of sorts based on the main-key. I need output as shown in the expected hash structure.

my @sort_by_rank;
for my $key1 (keys %rankBased) {
    for my $key2 (keys %{$rankBased{$key1}}) {
        @sort_by_rank = sort{ $rankBased{$b}{$key2}{score} <=> $rankBased{$a}{$key2}{score}
                            } keys %rankBased;
    }   
}

Here %rankBased is the hash.

Please help.


Solution

  • You can't sort a hash, but you can sort the list of a hash's keys.

    for my $student_id (keys %tests_by_student_id) {
       my $tests = $tests_by_student_id{$student_id};
       my @sorted_test_ids =
          sort { $tests->{$b}{score} <=> $tests->{$a}{score} }
             keys(%$tests);
    
       # Do something with the student's sorted tests here.
    }
    

    (Meaningless %rankBased renamed to %tests_by_student_id.)


    The above is great is you want to do something with each student. But if you actually want to "sort the hash", you'll need to switch to a different structure.

    for my $student_id (keys %tests_by_student_id) {
       # This next line is the same as 
       # my $tests = $tests_by_student_id{$student_id};
       # except changing $tests also changes
       # $tests_by_student_id{$student_id}.
       for my $tests ($tests_by_student_id{$student_id}) {
          $tests = [
             sort { $b->{score} <=> $a->{score} }
                map { { id => $_, %{ $tests->{$_} } } }
                   keys(%$tests)
          ];
       }
    }
    

    This produces

    $VAR1 = {
       '191' => [ { id => 'test3', score =>  15.62 },
                  { id => 'test4', score =>  11.84 },
                  { id => 'test1', score =>   9.18 },
                  { id => 'test2', score =>   2.84 } ],
       '190' => [ { id => 'test3', score =>  18.50 },
                  { id => 'test4', score =>  14.88 },
                  { id => 'test1', score =>  13.28 },
                  { id => 'test2', score => -47.56 } ],
    };