0

I have the following nested hash structure:

{12 => {:points=>0, :diff=>0},
 1=> {:points=>18, :diff=>57},
 4=>{:points=>12, :diff=>67},
 5=>{:points=>9, :diff=>62}}

I'd like to sort it by points first (descending) and by diff second (ascending).

I'm able to sort it only by one of those values using:

my_hash.sort_by {|participant_id, values| values[:points] }.reverse.to_h

but I have been unable to find a way to sort it by both values.

I've tried using:

my_hash.sort_by {|participant_id, values| values[:diff] or values[:points] }.reverse.to_h

Most answers regarding hash sorting consider one value like this and other cases doesn't seem to fit my purpose. Could you help finding a solution?

alopez02
  • 1,524
  • 2
  • 17
  • 36

2 Answers2

1

I would do this:

hash = {12=>{:points=>0, :diff=>0}, 1=>{:points=>18, :diff=>57}, 4=>{:points=>12, :diff=>67}, 5=>{:points=>9, :diff=>62}}

hash.sort_by { |_, v| [-v[:points], v[:diff]] }.to_h
#=> {1=>{:points=>18, :diff=>57}, 4=>{:points=>12, :diff=>67}, 5=>{:points=>9, :diff=>62}, 12=>{:points=>0, :diff=>0}}

Basically, it extracts the values to sort by into a structure like this: [[0,0], [-18,57], [-12,67], [-9,62]]. Note the - at -v[:points] which leads to a descending ordering. The second number is only taken into account if the first match.

spickermann
  • 100,941
  • 9
  • 101
  • 131
  • Great thanks, I was trying to do something similar by separating both values by commas but forgot to surround them by `[ ]`. Also didn't know you could use `-` to change the order. Thanks a lot for your help. – alopez02 Jun 26 '18 at 11:51
0

Have you tried this.

my_hash.sort_by {|participant_id, values| [values[:diff], values[:points]] }.reverse.to_h
Stephen M
  • 182
  • 2
  • 16