Reputation: 37
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!
Upvotes: 1
Views: 1476
Reputation: 110755
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" }]
Upvotes: 2
Reputation: 121010
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.
Upvotes: 3