Enumerable Methods
map, select, reduce, and the rest of the Enumerable module that makes Ruby collections so expressive.
3 min read
Arrays, hashes, and ranges all mix in a module called Enumerable, which is where methods like map, select, and reduce actually live. Once you understand a handful of these, you'll rarely write a manual loop to transform or filter a collection again.
map: transform every element
numbers = [1, 2, 3, 4, 5]
doubled = numbers.map { |n| n * 2 }
# [2, 4, 6, 8, 10]
names = ["ada", "grace"]
capitalized = names.map(&:capitalize)
# ["Ada", "Grace"]map always returns a new array of the same length, one output for each input. map(&:capitalize) is shorthand for map { |n| n.capitalize } — the &:symbol trick turns a method name into a block that calls that method on each element, and you'll see it everywhere in real Ruby code.
select / reject: filter
numbers = [1, 2, 3, 4, 5, 6]
numbers.select { |n| n.even? } # [2, 4, 6] -- keep matches
numbers.reject { |n| n.even? } # [1, 3, 5] -- keep non-matchesselect (aliased as filter) keeps elements where the block is truthy; reject is its exact opposite.
find / detect: the first match
numbers.find { |n| n > 3 } # 4 -- the first element matching, or nil if none matchUnlike select, find stops as soon as it finds one match and returns that single element, not an array.
reduce / inject: fold into one value
[1, 2, 3, 4].reduce(0) { |sum, n| sum + n } # 10
[1, 2, 3, 4].reduce(:+) # 10 -- shorthand with a symbol
[1, 2, 3, 4].reduce(1) { |product, n| product * n } # 24reduce (aliased inject) carries an accumulator through the collection, updating it each step, and returns the final value. The optional first argument (0, 1 above) is the accumulator's starting value; without it, reduce uses the first element as the starting point.
sort_by, group_by, and friends
users = [{ name: "Ada", age: 36 }, { name: "Grace", age: 41 }]
users.sort_by { |u| u[:age] }
# sorted by age ascending
users.group_by { |u| u[:age] > 40 ? :older : :younger }
# { younger: [...Ada's hash...], older: [...Grace's hash...] }
[1, 2, 3].sum # 6
[1, 2, 3].min # 1
[1, 2, 3].max # 3
[1, 2, 3].any? { |n| n > 2 } # true
[1, 2, 3].all? { |n| n > 0 } # true
[1, 2, 3].none? { |n| n > 5 } # trueany?, all?, and none? are worth calling out specifically — they read as a direct sentence ("do any of these satisfy this?") and replace a manual loop with a boolean flag that a surprising number of people still reach for out of habit.
Chaining
The real payoff of Enumerable is that these methods compose — each one returns a value another can operate on, so a multi-step transformation reads top to bottom instead of nesting:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.select(&:even?)
.map { |n| n * n }
.sum
# 220 -- sum of squares of even numbers 2..10This chained style — filter, transform, reduce — is one of the clearest markers of idiomatic Ruby, and it's a large part of why Ruby code is often praised as readable: each line states its intent, and the pipeline as a whole reads almost like a sentence.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.