-2

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.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • How do you want to store the sorted result? Hashes aren't sorted so, I guess that you want some sort of array? – Ted Lyngmo Mar 06 '21 at 17:08
  • The desired output doesn't make sense to me. You cannot really *sort* a hash. But you can sort the keys upon printing a hash's contents. Maybe that's what you're after? Then [edit] the question and show the code outputting the structure. – sticky bit Mar 06 '21 at 17:09
  • @TedLyngmo Can't we use 'Tie::IxHash'; to make the order of sorting remain same when storing to another hash. – Akhil paul Mar 06 '21 at 17:13
  • Possibly - that's beyond my knowledge. I only know basic perl and a few modules. `Tie::IxHash` isn't one of them :-) – Ted Lyngmo Mar 06 '21 at 17:15
  • @stickybit Given in the hash structure, as you can see 191 & 190 are the outer keys, which point to another set of hash. I want to sort only the inner hash. Is it possible to do so? – Akhil paul Mar 06 '21 at 17:15
  • @Akhilpaul: The inner ones are still hashes, aren't they? – sticky bit Mar 06 '21 at 17:16
  • @TedLyngmo Tie::IxHash can be defined with a hash, so that when data is stored to hash the order in which its stored is preserved. In other cases there is no preserved order. – Akhil paul Mar 06 '21 at 17:17
  • @stickybit Yes, the inner ones are hashes – Akhil paul Mar 06 '21 at 17:17
  • @stickybit The inner keys needs to be sorted, keeping the outer keys as it is. – Akhil paul Mar 06 '21 at 17:21
  • @Akhilpaul: That was a rhetoric question... But think about your answer and what it implies (for "normal" hashes). – sticky bit Mar 06 '21 at 17:21
  • @stickybit hmm, i don't quite get you – Akhil paul Mar 06 '21 at 17:23
  • @Akhilpaul: ... you cannot "normally" sort "normal" hashes (maybe there's some way manipulating the internals of the memory on low level, but that'd be beyond "normal" Perl programming), no matter how they are referenced or not... But why aren't you actually using `Tie::IxHash` if you think it can do what you want? Maybe [edit] the question and add that information. – sticky bit Mar 06 '21 at 17:26
  • @stickybit `Tie::IxHash` only preserves the data insertion order, i want to sort the inner hash as i said. – Akhil paul Mar 06 '21 at 17:33
  • 1
    @Akhilpaul: Hmm, there's a `Reorder` in the documentation of `Tie::IxHash`... But either way you really need to [edit] the question and elaborate. Tell us *what* problem you think you'd solve when "sorting" a hash (it might be an [XY problem](https://en.wikipedia.org/wiki/XY_problem)), show your attempts (maybe with `Tie::IxHash`) along side a **detailed** explanation what was wrong with these that attempts, did you get error message, was the result unexpected, etc.. – sticky bit Mar 06 '21 at 17:39

1 Answers1

3

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 } ],
};
ikegami
  • 367,544
  • 15
  • 269
  • 518