1

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!

kkir
  • 37
  • 7
  • 1
    Which animals are cats (or dogs) ? – Sagar Pandya Jul 26 '18 at 16:22
  • 1
    When you say you want cats to come before dogs, do you mean you want all all black dogs, then all orange dogs, then all golden dogs, then all mixed dogs, then all black cats, then all orange cats, then all golden cats, then all mixed cats? – Piccolo Jul 26 '18 at 16:23
  • @Piccolo Edited my post, hopefully its a bit more clear. Thanks! – kkir Jul 26 '18 at 16:36
  • Possible duplicate of [How to sort an array of hashes in ruby](https://stackoverflow.com/questions/5483889/how-to-sort-an-array-of-hashes-in-ruby) – jedi Jul 26 '18 at 20:25
  • 1
    @jedi, the question you reference is concerned with sorting an array of hashes on a single key's value. – Cary Swoveland Jul 27 '18 at 02:32
  • kkir, you don't need the reference to Ruby in the title; that's taken care of by your Ruby tag. – Cary Swoveland Aug 07 '18 at 06:16

2 Answers2

3

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.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
2

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" }]
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100