map, select, and reduce
Prerequisites: Blocks
These block-taking methods replace most hand-written loops in Ruby. They work on arrays, hashes, ranges, and anything else that's enumerable (can be iterated).
map — transform every element
map runs the block on each element and returns a new array of
the results:
prices = [10, 20, 30]
with_tax = prices.map { |price| price * 1.2 }
p with_tax
[12.0, 24.0, 36.0]
select and reject — filter
nums = (1..10).to_a
p nums.select { |n| n.even? }
p nums.reject(&:even?)
p nums.find { |n| n > 4 }
[2, 4, 6, 8, 10]
[1, 3, 5, 7, 9]
5
select keeps elements where the block is truthy; reject removes
them; find returns only the first match (or nil).
reduce — combine into one value
reduce walks the collection, carrying an accumulator along:
nums = [1, 2, 3, 4]
total = nums.reduce(0) { |sum, n| sum + n }
p total
10
The accumulator (sum) starts at 0; each pass returns the new
accumulator. For simple cases there are ready-made methods:
sum, min, max, count.
More everyday helpers
words = %w[apple avocado banana cherry blueberry]
p words.group_by { |w| w[0] }
p words.sort_by(&:length)
p words.any? { |w| w.start_with?("c") }
p words.all? { |w| w.length > 3 }
p %w[a b a c a].tally
{"a" => ["apple", "avocado"], "b" => ["banana", "blueberry"], "c" => ["cherry"]}
["apple", "banana", "cherry", "avocado", "blueberry"]
true
true
{"a" => 3, "b" => 1, "c" => 1}
Chaining
These methods return new collections, so they chain naturally:
orders = [120, 45, 300, 80]
big_total = orders.select { |o| o > 100 }.map { |o| o * 0.9 }.sum
p big_total
378.0
Common mistakes
- Using
eachwhen you want a result.eachreturns the original array, not the block's results — usemap. - Using
selectwhen you only need the first match.findstops as soon as it finds one.
Practice
Given names = ["ada", "linus", "grace", "al"], produce an array of
the names longer than 2 letters, capitalized.
Show solution
names = ["ada", "linus", "grace", "al"]
p names.select { |n| n.length > 2 }.map(&:capitalize)
["Ada", "Linus", "Grace"]