Today I Learned

hashrocket 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.

See More #ruby TILs
Looking for help? Each developer at Hashrocket has years of experience working with Ruby applications of all types and sizes. We're an active presence at Ruby conferences, have written some of the most popular gems, and have worked on many of the web's Ruby on Rails success stories. Contact us today to talk about your Ruby project.