2

I have one array of hashes as following:

[{"k1"=>"v1", "k2"=>"75.1%"}, {"k1"=>"v2", "k2"=>"-NA-"}, {"k1"=>"v3", "k2"=>"5.1%"}]

I want to sort this array of hashes based on the value of key "k2". I want to sort it in the decreasing order with "-NA-" coming at top always.

I want sorted array like this:

[{"k1"=>"v2", "k2"=>"-NA-"}, {"k1"=>"v1", "k2"=>"75.1%"}, {"k1"=>"v3", "k2"=>"5.1%"}]

I tried hash.sort_by method, but got incorrect results. How to do it in Ruby?

user2823083
  • 445
  • 2
  • 4
  • 10
  • From what you wrote, it sounds like you want to sort them as string. For example, `"5%"` would come after `"75%"` and before `"15%"` in descending order. Is this correct? – sawa Sep 28 '13 at 13:08
  • No. I want to sort them as numbers only. So, order will be : 75, then 15, 5. But NA should always come at top. – user2823083 Sep 28 '13 at 13:25
  • 1
    I see. Then the question was not well stated. Make it clear. – sawa Sep 28 '13 at 13:26
  • This was not a duplicate of the linked answer. I had the same question and the other answer is insufficient due to the requirement in this question that a certain value always be sorted to the beginning of the array. – Huliax Apr 08 '14 at 19:45

3 Answers3

4
a = [
  {"k1"=>"v1", "k2"=>"75.1%"},
  {"k1"=>"v2", "k2"=>"-NA-"},
  {"k1"=>"v3", "k2"=>"5.1%"}
]

na, rest = a.partition{|h| h["k2"] == "-NA-"}
na + rest.sort_by{|h| h["k2"].to_f}.reverse
# =>
[
  {
    "k1" => "v2",
    "k2" => "-NA-"
  },
  {
    "k1" => "v1",
    "k2" => "75.1%"
  },
  {
    "k1" => "v3",
    "k2" => "5.1%"
  }
]
sawa
  • 165,429
  • 45
  • 277
  • 381
1
ar = [{"k1"=>"v1", "k2"=>"75.1%"}, {"k1"=>"v2", "k2"=>"-NA-"}, {"k1"=>"v3", "k2"=>"5.1%"}]
ar.sort_by{|h|(k2 = h["k2"]) == "-NA-"? -Float::INFINITY : -k2.to_f}
#=>[{"k1"=>"v2", "k2"=>"-NA-"}, {"k1"=>"v1", "k2"=>"75.1%"}, {"k1"=>"v3", "k2"=>"5.1%"}]

Sorting of floats in descending order can be done by ordering them as their negatives. The block will treat "-NA-" as the most negative number: minus Float::INFINITY; all other strings are ordered like their negative float representations would, using the ternary operator (shorthand for if then else).

steenslag
  • 79,051
  • 16
  • 138
  • 171
1

Here's another (commonly used) approach (with a from @sawa):

NA = "-NA-"
K2 = "k2" 
a.sort {|a,b| a[K2]==NA ? -1 : b[K2]==NA ? 1 : -a[K2].to_f <=> -b[K2].to_f }
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100