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 }
Related videos on Youtube
![zengr](https://i.stack.imgur.com/tvzHg.jpg?s=256&g=1)
Author by
zengr
Software Engineer in San Fransisco Bay Area. #Java #Python #Generalist ↑ ↑ ↓ ↓ ← → ← → B A
Updated on July 05, 2022Comments
-
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 over 13 yearsI 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 over 13 yearsVisually this is cleaner but it causes an extra traversal of the collection to reverse it.
-
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 likea[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 over 13 yearsI 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 about 13 yearsI'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 about 13 yearsPS: see the Stack Overflow review policy on meta for more: meta.stackexchange.com/questions/74194/…
-
boddhisattva almost 11 years@glennmcdonald can you please tell me how can one calculate the timings for each call in ruby ?
-
boddhisattva almost 11 yearsI 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 almost 8 yearsWhat about
h.sort_by(&:last).reverse
? Any times on that? -
danielricecodes about 6 yearsWhich Ruby version?
-
Eric Duminil over 5 years@TamzinBlake: I really dont think
sort_by
mutates the hash. What did you mean exactly? -
Tamzin Blake over 5 yearsNice catch!
.sort_by!
mutates the hash,.sort_by
does not.