Descending sort by value of a Hash in Ruby

60,383

Solution 1

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

Solution 2

h.sort {|a,b| b[1]<=>a[1]}

Solution 3

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

Solution 4

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

Share:
60,383

Related videos on Youtube

zengr
Author by

zengr

Software Engineer in San Fransisco Bay Area. #Java #Python #Generalist ↑ ↑ ↓ ↓ ← → ← → B A

Updated on July 05, 2022

Comments

  • zengr
    zengr almost 2 years

    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?

  • PJP
    PJP over 13 years
    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!?"
  • PJP
    PJP over 13 years
    Visually this is cleaner but it causes an extra traversal of the collection to reverse it.
  • Chuck
    Chuck over 13 years
    @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.
  • PJP
    PJP over 13 years
    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.
  • Taryn East
    Taryn East about 13 years
    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
    Taryn East about 13 years
    PS: see the Stack Overflow review policy on meta for more: meta.stackexchange.com/questions/74194/…
  • boddhisattva
    boddhisattva almost 11 years
    @glennmcdonald can you please tell me how can one calculate the timings for each call in ruby ?
  • boddhisattva
    boddhisattva almost 11 years
    I found out how to calculate the time wrt each call. Here's a sample link:- ruby-doc.org/core-1.9.3/Enumerable.html#method-i-sort
  • Cruz Nunez
    Cruz Nunez almost 8 years
    What about h.sort_by(&:last).reverse? Any times on that?
  • danielricecodes
    danielricecodes about 6 years
    Which Ruby version?
  • Eric Duminil
    Eric Duminil over 5 years
    @TamzinBlake: I really dont think sort_by mutates the hash. What did you mean exactly?
  • Tamzin Blake
    Tamzin Blake over 5 years
    Nice catch! .sort_by! mutates the hash, .sort_by does not.