Array#map
The .map method (also known as .collect) transforms each element in an array by running a block of code for every element and collecting the results into a new array.
It is one of the most common methods in Ruby because it mirrors how people describe the work: take each element, turn it into something else, and keep the results. Changing types, pulling out attributes, and reshaping values before another step are all natural fits for map because the transformation stays visible in the block. When you do not need the transformed array, use each instead so the code does not pretend to collect a result that it will never use.
numbers = [1, 2, 3, 4, 5]
# Basic usage
squared = numbers.map { |n| n ** 2 }
# => [1, 4, 9, 16, 25]
# collect is an alias for map
doubled = numbers.collect { |n| n * 2 }
# => [2, 4, 6, 8, 10]
This first example shows the basic shape of the method: take a collection in, return a new collection out. That pattern is easy to recognize in Ruby code because the work stays focused on transformation instead of side effects.
Syntax
array.map { |element| block }
array.map do |element|
# block body
end
# With index
array.map.with_index { |element, index| block }
The with_index form is useful when the transformed value needs its position as part of the result. It keeps the index close to the block so you do not need extra loop bookkeeping. Choosing between the plain block and with_index depends on whether position matters for the transformed output. If the index is irrelevant, the simpler block form keeps the intent clearer.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| block | Block | Required | Code to run for each element |
What .map Returns
.map always returns a new array. The original array remains unchanged.
original = ["hello", "world"]
uppercased = original.map(&:upcase)
original # => ["hello", "world"] - unchanged
uppercased # => ["HELLO", "WORLD"] - new array
To modify the original array in place, use .map!, though this is rarely used in practice.
That difference matters because map is meant to build a fresh result, not mutate the source while you are still iterating over it. Keeping the original array intact makes the method easier to trust in pipelines and tests.
Common use cases
Transforming Data
# Convert strings to integers
ages = ["25", "30", "35"].map(&:to_i)
# => [25, 30, 35]
# Extract a specific attribute
users = [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
names = users.map { |u| u[:name] }
# => ["Alice", "Bob"]
This is the most common reason to use map: the input structure stays the same, but each element gets reshaped into a more convenient form. It is a good fit for type conversion and attribute extraction because the transformation reads directly in the block.
With .with_index
fruits = ["apple", "banana", "cherry"]
fruits.map.with_index { |fruit, i| "#{i + 1}. #{fruit}" }
# => ["1. apple", "2. banana", "3. cherry"]
The index is part of the output here, so the method gives you a simple way to number items without building a separate counter variable. That keeps the transformation and the numbering rule in the same place. Once the transformed result is ready, it often flows into another method call, and map is built for exactly that kind of pipeline.
Chaining with other methods
# map then select (keep only even results)
(1..10).map { |n| n * 2 }.select { |n| n % 4 == 0 }
# => [4, 8, 12, 16, 20]
# map with compact (remove nils)
[1, 2, nil, 3].map { |n| n&.to_s }
# => ["1", "2", nil, "3"]
Chaining works well when map is just one step in a larger cleanup flow. The important detail is that each method in the chain still has a single job, which keeps the code easy to read from left to right. That can make a longer pipeline feel surprisingly simple because each step still has one visible purpose.
Chaining works well when map is just one step in a larger cleanup flow. The important detail is that each method in the chain still has a single job, which keeps the code easy to read from left to right.
Using Symbol#to_proc
Ruby lets you use the &:method shorthand for simple transformations:
# These are equivalent
words.map { |w| w.upcase }
words.map(&:upcase)
numbers.map { |n| n.to_s }
numbers.map(&:to_s)
That shorthand is a nice fit when the transformation is already named by a method. It trims the block down to the part that matters most, which is the value being applied to each element.
.map vs .collect
There is no difference between .map and .collect. They are exactly the same method. Use whichever reads better in context:
# map reads well when transforming
results.map { |r| process(r) }
# collect reads well when collecting
results.collect { |r| r[:value] }
The important part is not the alias itself, but the shape of the code around it. map works best when the output is a new array that will be used later, and that simple rule makes it easy to spot when a loop is doing a transformation instead of a side effect.
Performance Notes
.mapcreates a new array, so for large datasets this uses memory- For simple transformations,
&:methodis a little faster than a block - Use
.eachwhen you don’t need the collected results
Gotchas
Forgetting the return value
# Common mistake: not using the return value
numbers.map { |n| puts n * 2 }
# => [nil, nil, nil, nil, nil]
# puts returns nil, so map collects nils
# Use each for side effects, map for transformations
This gotcha is worth remembering because map will happily collect whatever the block returns, including nil. If the goal is printing or logging, each usually makes the intent clearer. The same rule applies any time the block produces a side effect: reach for each when the return value is not needed, and save map for actual transformations.
map vs flat_map
# map returns array of arrays
[[1, 2], [3, 4]].map { |arr| arr.map { |n| n * 2 } }
# => [[2, 4], [6, 8]]
# flat_map flattens one level
[[1, 2], [3, 4]].flat_map { |arr| arr.map { |n| n * 2 } }
# => [2, 4, 6, 8]
The nested-versus-flat distinction is the part that usually matters in practice. If later code expects one level of values, flat_map saves a cleanup step; if the nesting is still meaningful, map keeps it intact.
flat_map is useful when the transformation produces nested arrays that should be flattened immediately. If you want to keep the nesting, stay with map; if you want one flat result, flat_map saves you a separate flatten call.