Enumerable#map
What does map do?
map transforms every element in a collection by passing it through a block, and returns a new array containing the results. The original collection is left unchanged.
Think of it as: “take each item, do something to it, and collect the results.”
Unlike each, which iterates purely for side effects and returns the original collection, map collects and returns what the block produces for each element.
Basic Usage
With a block
[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]
["hello", "world"].map(&:upcase) # => ["HELLO", "WORLD"]
Without a block
When called without a block, map returns an Enumerator:
[1, 2, 3].map # => #<Enumerator: [1, 2, 3]:map>
This allows chaining with other Enumerable methods:
[1, 2, 3, 4, 5].map.with_index { |n, i| [i, n] }
# => [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]
Practical Examples
Extracting attributes
users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Carol", age: 35 }
]
users.map { |u| u[:name] } # => ["Alice", "Bob", "Carol"]
users.map { |u| u[:age] } # => [30, 25, 35]
Converting types
["1", "2", "3"].map(&:to_i) # => [1, 2, 3]
[1.5, 2.7, 3.1].map(&:to_i) # => [1, 2, 3]
[100, 200, 300].map { |n| n.to_s } # => ["100", "200", "300"]
Transforming hashes
When called on a hash, map yields key-value pairs:
ages = { alice: 30, bob: 25, carol: 35 }
# Returns an array of arrays
ages.map { |name, age| [name, age * 2] }.to_h
# => { alice: 60, bob: 50, carol: 70 }
# Extract just the keys
ages.map { |name, _| name.to_s } # => ["alice", "bob", "carol"]
Nested mapping
matrix = [[1, 2], [3, 4], [5, 6]]
matrix.map { |row| row.map { |n| n * 10 } }
# => [[10, 20], [30, 40], [50, 60]]
Using with_index
fruits = ["apple", "banana", "cherry"]
fruits.map.with_index { |fruit, i| "#{i + 1}. #{fruit}" }
# => ["1. apple", "2. banana", "3. cherry"]
Symbol to proc shorthand
[1, 2, 3, 4, 5].map(&:succ) # => [2, 3, 4, 5, 6]
["hello", "world"].map(&:length) # => [5, 5]
map vs each: The Key Difference
The most common mistake is confusing map with each. They look similar but behave very differently:
| Method | Returns | Use case |
|---|---|---|
each | Original collection (unchanged) | Iterating for side effects |
map | New array of block results | Transforming elements |
numbers = [1, 2, 3]
result = numbers.each { |n| n * 2 }
result # => [1, 2, 3] (original, unchanged!)
result = numbers.map { |n| n * 2 }
result # => [2, 4, 6] (new array with transformed values)
When to use each
Use each when you want to perform side effects — printing, writing to a file, updating external state:
[1, 2, 3].each { |n| puts n * 2 }
# Output:
# 2
# 4
# 6
# => [1, 2, 3]
When to use map
Use map when you want to derive a new collection from an existing one:
names = ["alice", "bob", "carol"]
names.map(&:capitalize) # => ["Alice", "Bob", "Carol"]
collect: The Alias
collect is an exact synonym for map in Ruby. They are identical — same implementation, same return type, same behaviour. Use whichever reads more naturally in your context.
[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]
[1, 2, 3].collect { |n| n * 2 } # => [2, 4, 6]
map is more common in Ruby code because it reads as “transform each element into something.” collect is the historical name from Smalltalk, Ruby’s predecessor in this design.
# Both produce identical results:
numbers.map(&:to_s)
numbers.collect(&:to_s)
Chaining with Other Enumerable Methods
map returns an array, so you can chain it with other Enumerable methods:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.select(&:even?)
.map { |n| n * n }
# => [4, 16, 36, 64, 100]
# Filter out nils with compact
["1", "two", "3", nil, "four"].map { |s| s.to_i if s.is_a?(String) }.compact
# => [1, 3, 4]
Common chains
# Select then map (or map then select — order matters!)
[1, 2, 3, 4, 5].select(&:odd?).map { |n| n * 10 } # => [10, 30, 50]
[1, 2, 3, 4, 5].map { |n| n * 10 }.select(&:even?) # => [20, 40]
# Map then reduce
[1, 2, 3, 4].map { |n| n * 2 }.reduce(:+) # => 20
# Map then flatten
[[1, 2], [3, 4]].map { |arr| arr.map { |n| n + 1 } }
# => [[2, 3], [4, 5]]
Using flat_map
flat_map combines map and flatten in one pass:
[[1, 2], [3, 4]].map { |arr| arr.map { |n| n * 2 } }
# => [[2, 4], [6, 8]] (array of arrays)
[[1, 2], [3, 4]].flat_map { |arr| arr.map { |n| n * 2 } }
# => [2, 4, 6, 8] (flattened)
Performance Considerations
map has O(n) time complexity — it visits each element exactly once. It always processes the full collection and creates a new array of the same size.
Memory usage
map creates and returns a new array. For very large collections, this can be significant. If you only need to iterate once (e.g., printing), use each.
# Memory: creates a full new array
squares = (1..1_000_000).map { |n| n**2 } # lots of memory
# If you just need to print squares:
(1..1_000_000).each { |n| puts n**2 } # no extra array created
Lazy evaluation with lazy.map
For large or infinite collections, use lazy to defer evaluation:
# Eager: processes all immediately
(1..10).map { |n| n * 2 }.first(3) # => [2, 4, 6]
# (actually computes all, then takes first 3)
# Lazy: processes only what's needed
(1..Float::INFINITY).lazy.map { |n| n * 2 }.first(3)
# => [2, 4, 6]
# (stops after producing 3 results)
Hash Considerations
When map is called on a hash, it yields [key, value] pairs:
scores = { alice: 100, bob: 85, carol: 92 }
# By default, returns an array of two-element arrays
scores.map { |name, score| "#{name}: #{score}" }
# => ["alice: 100", "bob: 85", "carol: 92"]
# To get a hash back, convert with `to_h`
scores.map { |name, score| [name, score + 10] }.to_h
# => { alice: 110, bob: 95, carol: 102 }
Summary
maptransforms each element and returns a new arraycollectis an exact alias — use whichever reads bettereachreturns the original;mapreturns transformed results- Without a block,
mapreturns an Enumerator (lazy by default when chained) mapalways returns an array, even on hashes (useto_hif you need a hash back)
See Also
- /reference/enumerable/enumerable-select/ — Returns elements matching a condition (filter, not transform)
- /reference/enumerable/enumerable-reject/ — Returns elements not matching a condition
- /reference/enumerable/enumerable-filter-map/ — Combines filtering and transformation in one pass
- /reference/enumerable/enumerable-reduce/ — Collapses a collection into a single accumulated value