83

My input hash: h = { "a" => 20, "b" => 30, "c" => 10 }

Ascending sort: h.sort {|a,b| a[1]<=>b[1]} #=> [["c", 10], ["a", 20], ["b", 30]]

But, I need [["b", 30], ["a", 20], ["c", 10]]

How is can we make it work the other way around, what does <=> mean?

zengr
  • 38,346
  • 37
  • 130
  • 192

4 Answers4

200

You can have it cleaner, clearer and faster, all at once! Like this:

h.sort_by {|k,v| v}.reverse

I benchmarked timings on 3000 iterations of sorting a 1000-element hash with random values, and got these times:

h.sort {|x,y| -(x[1]<=>y[1])} -- 16.7s
h.sort {|x,y| y[1] <=> x[1]} -- 12.3s
h.sort_by {|k,v| -v} -- 5.9s
h.sort_by {|k,v| v}.reverse -- 3.7
glenn mcdonald
  • 15,290
  • 3
  • 35
  • 40
12
h.sort {|a,b| b[1]<=>a[1]}
ceth
  • 44,198
  • 62
  • 180
  • 289
  • 7
    I've downvoted this comment, not because the answer is wrong, but because you don't explain why this is the correct answer. The questioner even asked specifically what "<=>" means - so (s)he's clearly after some explanation of how this all works. It's a good idea to help out that way :) – Taryn East May 30 '11 at 17:01
  • PS: see the Stack Overflow review policy on meta for more: http://meta.stackexchange.com/questions/74194/how-to-review-can-we-agree-on-a-review-policy – Taryn East Jun 01 '11 at 13:36
11

Super simple: h.sort_by { |k, v| -v }

Paul Odeon
  • 4,407
  • 1
  • 37
  • 37
10

<=> compares the two operands, returning -1 if the first is lower, 0 if they're equal and 1 if the first is higher. This means that you can just do -(a[1]<=>b[1]) to reverse the order.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • 1
    I always prefer seeing it written with the "a" and "b" elements swapped, rather than negating the result. With them swapped I only have to look at their order to see they're backwards to know it's reversed. When the value of `<=>` is negated I still have to look at the actual comparison to know what is going on. It's a minor point but something I'm aware of because I can feel my brain have to do the second check after doing a "What!?" – the Tin Man Nov 24 '10 at 17:16
  • 1
    @Greg: I definitely see why you'd prefer it the other way. I'm the opposite: To my scanning eyes, `b[1]<=>a[1]` looks hella like `a[1]<=>b[1]` and I feel a need to stop and check, whereas the negation makes it immediately obvious that we're doing a reverse sort. – Chuck Nov 24 '10 at 20:40
  • I understand your point too. Either way of doing it still requires a careful look at the values being compared. Maybe we need a different operator - `>=<` for reverse order? Nah, that'd be just as bad. It's the entire construct, but I prefer `<=>` over a more verbose approach where we'd have to call some method names. – the Tin Man Nov 24 '10 at 21:49