How to sort a Ruby Hash by number value?

134,255

Solution 1

No idea how you got your results, since it would not sort by string value... You should reverse a1 and a2 in your example

Best way in any case (as per Mladen) is:

metrics = {"sitea.com" => 745, "siteb.com" => 9, "sitec.com" => 10 }
metrics.sort_by {|_key, value| value}
  # ==> [["siteb.com", 9], ["sitec.com", 10], ["sitea.com", 745]]

If you need a hash as a result, you can use to_h (in Ruby 2.0+)

metrics.sort_by {|_key, value| value}.to_h
  # ==> {"siteb.com" => 9, "sitec.com" => 10, "sitea.com", 745}

Solution 2

Since value is the last entry, you can do:

metrics.sort_by(&:last)

Solution 3

Already answered but still. Change your code to:

metrics.sort {|a1,a2| a2[1].to_i <=> a1[1].to_i }

Converted to strings along the way or not, this will do the job.

Solution 4

That's not the behavior I'm seeing:

irb(main):001:0> metrics = {"sitea.com" => 745, "siteb.com" => 9, "sitec.com" =>
 10 }
=> {"siteb.com"=>9, "sitec.com"=>10, "sitea.com"=>745}
irb(main):002:0> metrics.sort {|a1,a2| a2[1]<=>a1[1]}
=> [["sitea.com", 745], ["sitec.com", 10], ["siteb.com", 9]]

Is it possible that somewhere along the line your numbers are being converted to strings? Is there more code you're not posting?

Share:
134,255

Related videos on Youtube

Dustin M.
Author by

Dustin M.

Updated on July 28, 2020

Comments

  • Dustin M.
    Dustin M. almost 4 years

    I have a counter hash that I am trying to sort by count. The problem I am running into is that the default Hash.sort function sorts numbers like strings rather than by number size.

    i.e. Given Hash:

    metrics = {"sitea.com" => 745, "siteb.com" => 9, "sitec.com" => 10 }
    

    Running this code:

    metrics.sort {|a1,a2| a2[1]<=>a1[1]}
    

    will return a sorted array:

    [ 'siteb.com', 9, 'sitea.com', 745, 'sitec.com', 10]
    

    Even though 745 is a larger number than 9, 9 will appear first in the list. When trying to show who has the top count, this is making my life difficult. :)

    Any ideas on how to sort a hash (or an array even) by number value size?

    I appreciate any help.

    • fl00r
      fl00r over 14 years
      what ruby version do you use? your sort result is very strange
  • Mladen Jablanović
    Mladen Jablanović over 14 years
    or simply sort_by{|k,v| v}
  • Dustin M.
    Dustin M. over 14 years
    Ahh your right it looks like the result in my code was returning it as a string. Pesky data types. :) Sometimes I am just too close to the problem. Thanks.
  • Dustin M.
    Dustin M. over 14 years
    My number was returning as a string, that fixed it.. I had a2 and a1 in that order because I wanted the results to sort decending.. thanks for your feedback though.
  • Jacob Mattison
    Jacob Mattison over 14 years
    Yup. Occasionally I hear someone refer to Ruby as "untyped". Oh, no, it's definitely typed. It's just not statically typed. :)
  • Hady Elsahar
    Hady Elsahar over 11 years
    this is why i love Ruby ... such problem is pain in a head in other languages. so simple
  • engineerDave
    engineerDave about 11 years
    one note is that if your keys are symbols you might have to convert them to strings.
  • Marc-André Lafortune
    Marc-André Lafortune about 11 years
    @engineerDave: Did you mean if your values are symbols? In any case, that's incorrect as Symbols are comparable to one another.
  • engineerDave
    engineerDave about 11 years
    @Marc-AndréLafortune: Not if they can't be coerced to strings? I'm a little stymied on this one. :) Welcoming any clarification. Perhaps a visual example can clarify. gist.github.com/nacengineer/5577868
  • Marc-André Lafortune
    Marc-André Lafortune about 11 years
    @engineerDave: No idea how you get your results. h2.sort works in Ruby 1.9x or 2.0.
  • engineerDave
    engineerDave about 11 years
    @Marc-AndréLafortune: nevermind. must have been Gremlins in irb. its working now. thanks!
  • Redoman
    Redoman about 11 years
    don't forget you get an [] back instead of an {}
  • Elchin
    Elchin over 9 years
    if you want to revert it to hash for, I found the following working: metrics.sort_by{ |k, v| v }.inject(Hash.new){ |h, a| h.merge!({a[0] => a[1]}) }
  • Marc-André Lafortune
    Marc-André Lafortune over 9 years
    @Elchin: you can use metrics.sort_by{ |k, v| v }.reverse.to_h
  • Elchin
    Elchin over 9 years
    @Marc-AndréLafortune, totally my bad, didn't see #to_h! Thank you!
  • Gerry Gleason
    Gerry Gleason about 9 years
    Actually even simpler as: hash.sort_by(&:last) with the same caveat about getting an array of pairs vs. a Hash.
  • Tamer Shlash
    Tamer Shlash almost 9 years
    This is awesome! any referecne for the pre-definition of &:last?
  • lazybios
    lazybios almost 9 years
    Hi @Marc-AndréLafortune, I have a small question why add a underscore at begin of key. This coding style mean what?
  • Marc-André Lafortune
    Marc-André Lafortune almost 9 years
  • lazybios
    lazybios almost 9 years
    @Marc-AndréLafortune Thank you ! :)
  • Beni Cherniavsky-Paskin
    Beni Cherniavsky-Paskin almost 7 years
    sort_by(&:last) is effectively shorthand for sort_by {|x| x.last} stackoverflow.com/questions/1217088/…
  • bragboy
    bragboy over 6 years
    for reverse sort you may use metrics.sort_by{|k,v| -v}
  • Francisco Quintero
    Francisco Quintero over 4 years
    Awesome. I came up with this way which is ugly, IMO metrics.invert.sort.reverse.to_h.invert Using the #invert method of Hash class