简体   繁体   中英

Sorting Array of Hashes (Ruby)

If I have a method that adds two arrays of hashes together and then sorts them based on a specified order, is it possible to sort it again based on a second specified order?

If I my method looks something like:

def all_pets

     #set order by fur color
     order = ['Black', 'Orange', 'Golden', 'Mixed']

     all_pets = dogs + cats #two arrays being added together

     all_pets.sort_by {|r| order.index(r[:fur_color])}

end

My output currently looks like:

  [{:name=>"Kitty", :fur_color=>"Black"} #cat,
  {:name=>"Fido", :fur_color=>"Black"} #dog, 
  {:name=>"Whiskers", :fur_color=>"Black"} #cat, 
  {:name=>"Buttons", :fur_color=>"Orange"} #cat, 
  {:name=>"Mr.Cat", :fur_color=>"Golden"} #cat, 
  {:name=>"Sparky", :fur_color=>"Golden"} #dog,
  {:name=>"Max", :fur_color=>"Mixed"} #dog]

This is great so far, I have the animals sorted by fur color exactly how I want, is it possible to then sort so cats will always come after dogs?

The two arrays being added have the exact same attributes (name and fur color), so I'm not sure how to order them by fur color first, and animal type second.

Since both arrays have the same attributes, and no animal type attribute, would it be possible to do something that puts all hashes that came from the array cat to come after the hashes from array dog after they've been sorted by fur color?

So my goal output would be:

  [{:name=>"Fido", :fur_color=>"Black"} #dog,
   {:name=>"Kitty", :fur_color=>"Black"} #cat, 
  {:name=>"Whiskers", :fur_color=>"Black"} #cat, 
  {:name=>"Buttons", :fur_color=>"Orange"} #cat, 
  {:name=>"Sparky", :fur_color=>"Golden"} #dog,
  {:name=>"Mr.Cat", :fur_color=>"Golden"} #cat, 
  {:name=>"Max", :fur_color=>"Mixed"} #dog]

Thanks!

Just pass an array to sort_by :

all_pets.sort_by {|r| [order.index(r[:fur_color]), r[:name]] }

The above will sort by the color and by the name inside the same color .

all_pets.sort_by do |r|
  [
    order.index(r[:fur_color]),
    cats.map { |c| c[:name] }.include?(r[:name])
    # or cats.include?(r)
  ]
end

The latter will put cats after dogs.

Here we can make use of the fact that, when used without a block, Enumerable#sort_by returns an enumerator.

COLOR_ORDER = ['Black', 'Orange', 'Golden', 'Mixed'].each_with_index.to_h
  #=> {"Black"=>0, "Orange"=>1, "Golden"=>2, "Mixed"=>3}

def sort_pets(pets)
  pets.sort_by.with_index { |pet, idx| [COLOR_ORDER[pet[:fur_color]], idx] }
end

dogs = [{ :name=>"Sparky",   :fur_color=>"Golden" },
        { :name=>"Max",      :fur_color=>"Mixed"  }, 
        { :name=>"Fido",     :fur_color=>"Black"  }]

cats = [{ :name=>"Kitty",    :fur_color=>"Black"  }, 
        { :name=>"Whiskers", :fur_color=>"Black"  }, 
        { :name=>"Buttons",  :fur_color=>"Orange" },
        { :name=>"Mr.Cat",   :fur_color=>"Golden" }]

sort_pets(dogs + cats)
  #=> [{:name=>"Fido",     :fur_color=>"Black "},
  #    {:name=>"Kitty",    :fur_color=>"Black" },
  #    {:name=>"Whiskers", :fur_color=>"Black" },
  #    {:name=>"Buttons",  :fur_color=>"Orange"},
  #    {:name=>"Sparky",   :fur_color=>"Golden"},
  #    {:name=>"Mr.Cat",   :fur_color=>"Golden"},
  #    {:name=>"Max",      :fur_color=>"Mixed" }]

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