Array#map

Updated April 1, 2026 · Array Methods
ruby array map transform collect stdlib

What does map do?

map transforms every element in an array by passing it through a block, and returns a new array containing the results. The original array is left unchanged.

Think of it as: “take each item, do something to it, and collect the results.”

Array#map is defined directly on the Array class, but Array includes Enumerable — so it behaves identically to Enumerable#map. Array overrides it with an optimised implementation.

Basic Usage

With a block

[1, 2, 3].map { |n| n * 2 }           # => [2, 4, 6]
["hello", "world"].map(&:upcase)      # => ["HELLO", "WORLD"]
[1, 2, 3, 4, 5].map { |n| n**2 }      # => [1, 4, 9, 16, 25]

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]]

collect: The Alias

collect is an exact synonym for map in Ruby. Same implementation, same return type, same behaviour. Use whichever reads more naturally.

[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 modern Ruby code. collect is the historical name inherited from Smalltalk, Ruby’s predecessor in this design.

# Both are identical:
numbers.map(&:to_s)
numbers.collect(&:to_s)

map vs each: The Key Difference

The most common source of confusion is mixing up map with each. They look similar but behave very differently:

MethodReturnsUse case
eachOriginal array (unchanged)Iterating for side effects
mapNew array of block resultsTransforming 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"]

Using map with Index

Chain with_index to access the index of each element:

fruits = ["apple", "banana", "cherry"]

fruits.map.with_index { |fruit, i| "#{i + 1}. #{fruit}" }
# => ["1. apple", "2. banana", "3. cherry"]

fruits.map.with_index { |fruit, i| [i, fruit] }
# => [[0, "apple"], [1, "banana"], [2, "cherry"]]

You can also combine with each_with_index when you need the index during iteration:

tasks = ["write", "review", "deploy"]

tasks.each_with_index.map { |task, i| "#{i + 1}. #{task}" }
# => ["1. write", "2. review", "3. deploy"]

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"]

Symbol to proc shorthand

[1, 2, 3, 4, 5].map(&:succ)           # => [2, 3, 4, 5, 6]
["hello", "world"].map(&:length)     # => [5, 5]

Nested mapping

Transform nested arrays by mapping over each inner array:

matrix = [[1, 2], [3, 4], [5, 6]]

matrix.map { |row| row.map { |n| n * 10 } }
# => [[10, 20], [30, 40], [50, 60]]

Flattening after mapping

pairs = [[1, 2], [3, 4], [5, 6]]

pairs.map { |a, b| a + b }           # => [3, 7, 11]
pairs.map { |a, b| [a * 2, b * 2] }  # => [[2, 4], [6, 8], [10, 12]]

Chaining with Other Array 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]

Common chains

# Select then map
[1, 2, 3, 4, 5].select(&:odd?).map { |n| n * 10 }  # => [10, 30, 50]

# Map then select — order matters!
[1, 2, 3, 4, 5].map { |n| n * 10 }.select(&:even?)  # => [20, 40]

# Map then reject
[1, 2, 3, 4, 5].map { |n| n * 2 }.reject { |n| n < 5 }
# => [6, 8, 10]

# Map then flatten
[[1, 2], [3, 4]].map { |arr| arr.map { |n| n + 1 } }
# => [[2, 3], [4, 5]]

Using compact after map

Remove nil values after a conditional transformation:

["1", "two", "3", nil, "four"].map { |s| s.to_i if s.is_a?(String) }.compact
# => [1, 3, 4]

Combining with sum

prices = [10, 20, 30]
prices.map { |p| p * 1.2 }.sum  # => 72.0  (with tax)

Mutating with map! (Bang Variant)

The bang variant map! mutates the original array in place instead of returning a new one:

numbers = [1, 2, 3, 4, 5]

result = numbers.map! { |n| n * 2 }

numbers  # => [2, 4, 6, 8, 10]
result   # => [2, 4, 6, 8, 10]
result.equal?(numbers)  # => true  (same object)

When to use map!

Use map! when you want to reuse the same variable rather than allocating a new array:

data = [1, 2, 3, 4, 5]

data.map!(&:to_s)
data  # => ["1", "2", "3", "4", "5"]

Comparing map vs map!

original = [1, 2, 3, 4, 5]

# map returns a new array, original unchanged
transformed = original.map { |n| n * 2 }
original      # => [1, 2, 3, 4, 5]
transformed  # => [2, 4, 6, 8, 10]

# map! modifies the original
original.map! { |n| n * 2 }
original  # => [2, 4, 6, 8, 10]

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 allocation can matter:

# 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

Bang variant performance

map! avoids allocating a new array, which reduces memory pressure for large arrays. However, it still iterates through all elements — the performance benefit is solely in avoiding allocation.

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)

See Also