Question

I'm confused as to how to use the sort_by method. How do I rearrange this:

@final_leaderboard = {
                     "Patriots" => {"Win"=>3, "Loss"=>0},
                     "Broncos" => {"Win"=>1, "Loss"=>1},
                     "Colts" => {"Win"=>0, "Loss"=>2},
                     "Steelers" => {"Win"=>0, "Loss"=>1},
                     }

to produce this:

Patriots  3   0
Broncos   1   1
Steelers  0   1
Colts     0   2

So far, I have this code, but it's reversed, and the Loss values are incorrect. What I want to do is to sort first by the winning conditions. The team with the most wins will be at the first item in the @final_leaderboard hash, follwed by Broncos with 1 win and then Steelers and Colts. However, I also want to sort by the losses too. For example, I would like the Steelers be listed as the third item in the @final_leaderboard hash because it has one loss as opposed to the colts who have two.

@final_leaderboard = @final_leaderboard.sort_by do |key,value|
  value["Loss"] <=> value["Win"]
end

The code snippet above will produce this output:

Patriots 3  0
Broncos  1  1
Colts    0  2
Steelers 0  1

The last two items are incorrect but I don't know what needs to be changed in the code snippet for the Steelers and Colts to be reversed. I am not familiar with the sort_by method and the sort_by method example/explanation on Ruby Docs do not have a visual example like the other methods, so I'm not too sure how to use it. If someone could please explain to me how to sort the last two items I would appreciate it. Thank you.

Was it helpful?

Solution 2

<=> returns only 1, 0, or -1 regardless of difference between two operands:

1 <=> 7    # => -1
1 <=> 5    # => -1
3 <=> 2    # => 1
3 <=> 0    # => 1

You'd better to use - operator instead.

In addition to that, sort, sort_by yield little value first. To list higher score (win - loss) first, you need negate the score.

Inaddition to that, to get exact same output you want, you need format the output using String%# or sprintf:

board.sort_by { |key,value| value['Loss'] - value['Win'] }.each do |key, value|
  puts "%-10s %2s %3s" % [key, value['Win'], value['Loss']]
end

output:

Patriots    3   0
Broncos     1   1
Steelers    0   1
Colts       0   2

UPDATE

This answer is based on wrong assumption about the rank. Please see sawa's answer.

OTHER TIPS

This code does what you explained:

@final_leaderboard.sort_by{|_, v| [-v["Win"], v["Loss"]]}

Result

[
  ["Patriots", {"Win"  => 3, "Loss" => 0}],
  ["Broncos", {"Win"  => 1, "Loss" => 1}],
  ["Steelers", {"Win"  => 0, "Loss" => 1}],
  ["Colts", {"Win"  => 0, "Loss" => 2}]
]

When you want to sort by multiple conditions as in your case, you use an array in sort_by block. The [-v["Win"], v["Loss"]] given here instructs to first sort by comparing the -v["Win"] of each hash (where v is the hash), which is to sort by descending order of Win, then when there is a tie, it looks at v["Loss"], which is to sort by ascending order of Loss.

If you mean sort first by Win number and then the Loss number (order by Win desc, Loss asc), then you may do like below:

@final_leaderboard = @final_leaderboard.sort do |a, b|
  a[1]['Win'] == b[1]['Win'] ? a[1]['Loss'] <=> b[1]['Loss'] :  b[1]['Win'] <=> a[1]['Win']
end

Note that while sawa's answer is quite correct:

@final_leaderboard.sort_by{|_, v| [-v["Win"], v["Loss"]]}

You have shown:

@final_leaderboard = @final_leaderboard.sort_by do |key,value|
  value["Loss"] <=> value["Win"]
end

So you probably want to use #sort_by! to modify @final_leaderboard.

@final_leaderboard.sort_by! { |_, v| [-v["Win"], v["Loss"]] }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top