Today I Learned

A Hashrocket project

Ruby Array#sort_by

Let’s say we have the following list of very interesting items.

items = [
  { name: "dogfood", price: 4.99 },
  { name: "catfood", price: 5.99 },
  { name: "batfood", price: 4.99 }
]

Our client wants the list sorted by price descending, and we should break ties in price by then sorting by item name descending. So with our example list, the result should be:

items = [
  { name: "batfood", price: 4.99 },
  { name: "dogfood", price: 4.99 },
  { name: "catfood", price: 5.99 }
]

Here’s a method that does the job for our client:

def sort_by_price_and_name(items)
  items
    .group_by { |i| i[:price] }
    .map { |_k,v| v.sort { |a, b| a[:name] <=> b[:name] } }
    .flatten
end

It’s a bit unwieldy and inelegant. It turns out there is a perfect method for this in Ruby:

def sort_by_price_and_name(items)
  items.sort_by { |i| [i[:price], i[:name]] }
end

Ruby’s Enumerable#sort_by works in this case where we sort by all the fields in ascending order. If we wanted to sort by price ascending, then name descending, sort_by would no longer be the tool we’d want to use.

Hat tip to @joshcheek for the knowledge.

Looking for help? Each developer at Hashrocket has years of experience working with Ruby applications of all types and sizes. Contact us and find out how we can help you.