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] # CaraThe shorthand also works: words.sort_by(&:length).
Challenge
MediumThe array products is given, each element a hash with :name, :price, and :quantity. Each product's stock value is price * quantity.
Print three lines:
- Names sorted by stock value descending, joined with
,(usesort_byon the derived value) - The name of the product with the highest quantity among items priced under 5 (use
max_byon a filtered list) - The name of the cheapest product whose quantity is at least 10 (use
min_byon a filtered list)
For the default array, the output is:
cheese, milk, bread, apple
bread
breadCheat 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)
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Strings In Depth
String Methods OverviewString InterpolationIterating Over StringsSplit and JoinRecap - String Weaver4Blocks, Procs & Lambdas
What is a Block?do..end vs BracesThe yield KeywordBlock ParametersProcs and LambdasRecap - Custom Iterator7Hashes Part 2
Hash.new with DefaultsIterating HashesNested HashesMerging and TransformingRecap - Frequency Counter10Project - Student Records
Project OverviewAdd Student5Enumerable Powerhouse
Select and RejectChaining MapReduce / Injectcount, all?, any?, none?group_by and partitionsort_by, min_by, max_byRecap - Data Pipeline8Advanced Decision Making
Case with Classes & RegexMulti-value whenTernary OperatorInline if / unlessRecap - Grade Classifier