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?

4
  • 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? Commented Sep 28, 2013 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. Commented Sep 28, 2013 at 13:25
  • 1
    I see. Then the question was not well stated. Make it clear. Commented Sep 28, 2013 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. Commented Apr 8, 2014 at 19:45

3 Answers 3

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%"
  }
]
Sign up to request clarification or add additional context in comments.

4 Comments

Superb answer, sawa :) I wasn't aware of the partition method.
"5.4&*$#%&+()".to_f => 5.4. Learning something new every day...
My apologies. Somehow I downvoted when I meant to upvote. Was +2 before I voted.
I corrected my mistake after your edit.
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).

2 Comments

Interesting answer. Can you explain it?
@AGS added some explanation.
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 }

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.