Menu
Coddy logo textTech

sort_by, min_by, max_by

Part of the Logic & Flow section of Coddy's Ruby journey — lesson 26 of 56.

You already know sort, min, and max. The _by versions give you control over what to compare, perfect for sorting structured data.

sort_by takes a block; the block returns the value to compare for each element:

words = ["banana", "hi", "hello"]
by_length = words.sort_by { |w| w.length }
puts by_length.inspect   # ["hi", "hello", "banana"]

For descending order, negate the comparison key, -w.length for numbers, or call reverse on the result.

min_by and max_by return the single element with the smallest / largest value of the block:

people = [
  { name: "Ada",   age: 36 },
  { name: "Bob",   age: 22 },
  { name: "Cara",  age: 41 }
]

youngest = people.min_by { |p| p[:age] }
oldest   = people.max_by { |p| p[:age] }
puts youngest[:name]   # Bob
puts oldest[:name]     # Cara

The shorthand also works: words.sort_by(&:length).

challenge icon

Challenge

Medium

The array products is given, each element a hash with :name, :price, and :quantity. Each product's stock value is price * quantity.

Print three lines:

  1. Names sorted by stock value descending, joined with , (use sort_by on the derived value)
  2. The name of the product with the highest quantity among items priced under 5 (use max_by on a filtered list)
  3. The name of the cheapest product whose quantity is at least 10 (use min_by on a filtered list)

For the default array, the output is:

cheese, milk, bread, apple
bread
bread

Cheat sheet

Use sort_by, min_by, and max_by to compare elements by a derived value via a block:

words = ["banana", "hi", "hello"]
words.sort_by { |w| w.length }   # ["hi", "hello", "banana"]
words.sort_by(&:length)          # shorthand

For descending order, negate numeric keys or call .reverse:

words.sort_by { |w| -w.length }  # ["banana", "hello", "hi"]

min_by / max_by return the single element with the smallest / largest block value:

people = [{ name: "Ada", age: 36 }, { name: "Bob", age: 22 }]

people.min_by { |p| p[:age] }  # { name: "Bob", age: 22 }
people.max_by { |p| p[:age] }  # { name: "Ada", age: 36 }

Chain with select/filter to operate on a subset first:

people.select { |p| p[:age] > 25 }.min_by { |p| p[:age] }

Try it yourself

products = [
  { name: "milk",   price: 3, quantity:  8 },
  { name: "apple",  price: 1, quantity:  4 },
  { name: "cheese", price: 5, quantity:  6 },
  { name: "bread",  price: 2, quantity: 12 }
]

# TODO: sort_by stock value desc; max_by quantity (price < 5); min_by price (quantity >= 10)
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Logic & Flow