简体   繁体   中英

Sort array of hashes by property AND exact match

Let's say I've got the following array of hashes:

arr = [{:name=>"foo", :value=>20},
       {:name=>"bar", :value=>25}, 
       {:name=>"baz", :value=>30}] 

I'm currently sorting by value like so:

arr.sort{|a,b| b[:value] <=> a[:value] }

Is it possible to move an element (ie the one in which name == 'bar' ) to the top of the stack after sorting without chaining another method? Ideally, this would just be some more in the sort block.

fast solution (can be refactored, I think)

arr.sort{|a,b| a[:name] == 'bar' ? -1 : b[:name] == 'bar' ? 1 : b[:value] <=> a[:value] }
# => [{:name=>"bar", :value=>25}, {:name=>"baz", :value=>30}, {:name=>"foo", :value=>20}] 

First, you should use a less verbose (and more efficient) Schwartzian transform with Enumerable#sort_by :

arr.sort_by { |h| -h[:value]] }

Now, taking advantage of the lexicographical order defined by arrays, what you asked for may be written:

arr.sort_by { |h| [h[:name] == "bar" ? 0 : 1, -h[:value]] }
arr.sort do |a,b|
  if a[:name] == "bar" && b[:name] != "bar"
    # a is bar but b is not, put a to the top, i.e. a is smaller than b
    -1
  elsif a[:name] != "bar" && b[:name] == "bar"
    # a is not bar but b is, put b to the top, i.e. a is bigger than b
    1
  else
    # both a and b are "bar", or bot are not "bar", than we can sort them normally
    a[:value] <=> b[:value]
  end
end

a dirty solution, thinking how to write better...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM