1

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.

Dan Rubio
  • 4,709
  • 10
  • 49
  • 106
  • I don't understand. Why did my question get downrated? – Dan Rubio Mar 03 '14 at 07:04
  • It is not clear what condition you want to sort by. You must understand that, since your code does not work, presenting it does not help. You need to explain it. Also, even if your code worked (which is not the case here), you should not present that before explaining what it is supposed to do. – sawa Mar 03 '14 at 07:04
  • 1
    Ok, I'll add that to my post. Thanks for the feedback – Dan Rubio Mar 03 '14 at 07:06
  • 1
    Dan, what's the rush in selecting an answer? A rush to judgement may discourage other, possibly better, answers. Also, it is more than a little annoying to be working on an answer and see the green check mark appear only minutes after the question was posted. – Cary Swoveland Mar 03 '14 at 07:39
  • My apologies. I'm an aspiring novice software/web developer and I'm relatively new to stack overflow and stack etiquette. I'll be sure to hold off next time. I just clicked that one cuz it did exactly what I wanted. – Dan Rubio Mar 03 '14 at 07:44

4 Answers4

4

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.

sawa
  • 165,429
  • 45
  • 277
  • 381
0

<=> 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.

Community
  • 1
  • 1
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • faletru, thanks so much for your help. I'm not sure I completely understand what that code snippet did but it solved my problem. I appreciate your help. I think I'm going to look back at this explanation when its not 2:22 AM. – Dan Rubio Mar 03 '14 at 07:19
  • @DanRubio, In my answer I assumed the rank is determined by virtual score (Win count - Loss count). You need adjust that part. – falsetru Mar 03 '14 at 07:23
  • This answer is wrong. For example, when there are two players A (Win 2, Loss 1) and B (Win 3, Loss 2), the answer here does not guarantee that B comes first. – sawa Mar 03 '14 at 07:28
  • @sawa, Thank you for comment. It's easy to fix. I will fix that when I come home. – falsetru Mar 03 '14 at 07:35
  • @sawa, I updated the answer. I posted the answer before the question edit, and didn't noticed that it was changed. Thank you. – falsetru Mar 03 '14 at 08:24
  • 2
    The question did not change. It had not been made clear what it was asking for. You took the risk of making a wild guess, which turned out to be wrong. And now, the part updated three minutes ago of your answer is no different from mine, which was answered about an hour ago after the OP had made the question clear. – sawa Mar 03 '14 at 08:26
  • @sawa, Yup. after question clarification (sorry for choosing poor word). Anyway, I tried to explain why the OP didn't get what he wanted. – falsetru Mar 03 '14 at 08:28
  • @sawa, Because I just arrived at home, and that is the best that I can think of. – falsetru Mar 03 '14 at 08:33
  • 1
    @sawa, Removed the code, and made reference to your answer. – falsetru Mar 03 '14 at 08:38
0

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
xdazz
  • 158,678
  • 38
  • 247
  • 274
0

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"]] }
Chris
  • 26,361
  • 5
  • 21
  • 42