Question
What does the .map method do in this Ruby expression?
params = (0...param_count).map
In particular, what values does the range (0...param_count) contain, and what happens when .map is called without a block?
Short Answer
Ruby's map method visits each item in a collection and builds a new collection from the values returned by a block. In this example, (0...param_count) is an exclusive range of integers. Because no block is supplied to .map, Ruby returns an Enumerator instead of immediately creating an array.
Concept
map is an Enumerable method in Ruby. Its job is to transform every item in a collection.
You give map a block. Ruby runs that block once per item, collects each block result, and returns those results in a new array.
numbers = [1, 2, 3]
doubled = numbers.map { |number| number * 2 }
# doubled is [2, 4, 6]
The original numbers array is unchanged. map creates a separate result.
In the expression below:
params = (0...param_count).map
(0...param_count) is a range that starts at 0 and stops before param_count. However, map has no block telling it how to transform each number. Ruby therefore returns an Enumerator, an object that represents work that can be performed later.
To produce an array, provide a block:
params = (0...param_count).map { |index| "param_#{index}" }
If param_count is 3, params becomes:
["param_0", "param_1", "param_2"]
Mental Model
Think of map as a factory conveyor belt.
- A collection supplies items to the belt.
- The block is the machine that changes each item.
mapputs every changed item into a new box (an array).
For example, a belt receives 0, 1, and 2. A label-making machine turns them into "param_0", "param_1", and "param_2".
If you call map without supplying the machine (a block), Ruby gives you an Enumerator: it is like receiving a scheduled conveyor belt that has not started processing items yet.
Syntax and Examples
The usual map syntax is:
new_array = collection.map { |item| transformation }
You can also use do and end:
new_array = collection.map do |item|
transformation
end
Transform an array
names = ["ada", "grace", "linus"]
capitalized_names = names.map { |name| name.capitalize }
puts capitalized_names.inspect
# ["Ada", "Grace", "Linus"]
For each string, the block calls capitalize. The returned strings become the contents of the new array.
Transform a range
squares = (1..4).map { |number| number * number }
puts squares.inspect
# [1, 4, 9, 16]
The inclusive range 1..4 contains 1, 2, 3, and 4.
Use the range from the question
param_count = 3
params = (0...param_count).map { |index| "param_#{index}" }
puts params.inspect
# ["param_0", "param_1", "param_2"]
The three-dot range, 0...param_count, excludes the ending value. With param_count = 3, it produces 0, 1, and 2.
Step by Step Execution
Consider this code:
param_count = 3
params = (0...param_count).map { |index| "param_#{index}" }
Execution proceeds as follows:
param_countis assigned3.- Ruby creates the exclusive range
0...3. - That range yields the values
0,1, and2. It does not yield3. mapcalls the block withindex = 0.- The block returns
"param_0".
- The block returns
mapcalls the block withindex = 1.- The block returns
"param_1".
- The block returns
mapcalls the block withindex = 2.- The block returns
"param_2".
- The block returns
mapgathers the returned values into a new array and assigns it toparams.
The final value is:
["param_0", "param_1", "param_2"]
Without a block:
params = (0...param_count).map
params is an Enumerator, not an array of transformed values. You can inspect it with:
puts params.class
# Enumerator
Real World Use Cases
map is useful whenever one input item should become one output item.
-
Formatting API data: Convert database records into JSON-friendly hashes.
users.map { |user| { id: user.id, name: user.name } } -
Building request parameters: Create a list of parameter names from a count.
(0...5).map { |i| "field_#{i}" } -
Converting user input: Turn text values into integers.
["10", "20", "30"].map(&:to_i) -
Generating file names: Create predictable paths for exported reports.
(1..3).map { |page| "report-page-#{page}.pdf" } -
Preparing view data: Convert domain objects into display strings.
products.map { |product| "#{product.name}: $#{product.price}" }
Real Codebase Usage
In production Ruby code, map is often used to shape data at boundaries: before rendering a view, sending an API response, writing a file, or passing values to another service.
Convert records into response data
response = users.map do |user|
{
id: user.id,
email: user.email,
active: user.active?
}
end
This keeps an API response independent from the full database object.
Use map with filter
A common pattern is to remove unwanted values first, then transform the remaining values:
active_emails = users
.select(&:active?)
.map(&:email)
select chooses which objects remain; map changes those objects into email strings.
Use the symbol-to-proc shortcut carefully
When calling a no-argument method on every item, Ruby supports a compact form:
names = users.map(&:name)
It is equivalent to:
names = users.map { |user| user.name }
Use the explicit block when the transformation has several steps or a descriptive variable improves readability.
Use lazy enumeration for large or unbounded input
Calling map without a block can be part of a chain that receives a block later:
first_three_squares = (1..).map.lazy { |n| n * n }.first(3)
# [1, 4, 9]
For ordinary finite collections, supply the block directly to map.
Common Mistakes
Forgetting the block
This does not create an array:
params = (0...3).map
puts params.class
# Enumerator
Provide a transformation block when you want results now:
params = (0...3).map { |i| "param_#{i}" }
Expecting map to change the original array
numbers = [1, 2, 3]
numbers.map { |n| n * 2 }
puts numbers.inspect
# [1, 2, 3]
Save the return value:
doubled = numbers.map { |n| n * 2 }
Ruby also has map!, which mutates an array in place, but use it only when changing the original array is intentional.
Using .. when ... is needed
(0..3).to_a
# [0, 1, 2, 3]
(0...3).to_a
# [0, 1, 2]
For indexes and counts, 0...count is usually correct because an array with count elements has valid indexes from 0 through count - 1.
Returning the wrong value from a multi-line block
map collects the final expression from each block execution.
labels = [1, 2].map do |number|
text = "Item #{number}"
puts text
end
# labels is [nil, nil]
puts returns nil. Make the desired value the final expression:
labels = [1, 2].map do |number|
text = "Item #{number}"
puts text
text
end
Using map only for side effects
If you only want to print, log, or send an action for every item, use each:
users.each { |user| puts user.name }
Use map when the transformed array is meaningful.
Comparisons
| Tool | Main purpose | Return value | Example |
|---|---|---|---|
map | Transform every item | A new array | `[1, 2].map { |
each | Perform an action for every item | The original collection | `[1, 2].each { |
select / filter | Keep matching items | A new array of kept items | [1, 2, 3].select(&:odd?) → [1, 3] |
reduce | Combine items into one result | One accumulated value | [1, 2, 3].reduce(:+) → 6 |
.. versus ... ranges
| Range | Includes ending value? | param_count = 3 result |
|---|---|---|
0..param_count | Yes | 0, 1, 2, 3 |
0...param_count | No | 0, 1, 2 |
map versus map!
| Method | Changes original array? | When to use it |
|---|---|---|
map | No | Prefer this for predictable, non-mutating transformations. |
map! | Yes | Use only when replacing the original array is intended. |
Cheat Sheet
# Transform each item and return a new array
result = collection.map { |item| item }
# Range with an exclusive ending value
indexes = (0...count).map { |i| i }
# Range with an inclusive ending value
numbers = (1..4).map { |n| n * 2 }
# Short form: call one method on every item
names = users.map(&:name)
# No block: returns an Enumerator
enumerator = [1, 2, 3].map
mapruns its block once for each item.- The block's final expression becomes the corresponding result item.
mapnormally returns a new array.mapdoes not modify the original collection.0...countis ideal for zero-based indexes: it yields exactlycountnumbers.- Use
eachfor side effects; usemapfor transformations.
FAQ
What does .map do in Ruby?
It transforms every item in an enumerable collection using a block and returns the transformed results in a new array.
What does (0...param_count).map return without a block?
It returns an Enumerator. Ruby delays the mapping operation until you provide work to perform, chain it, or otherwise consume it.
Why use 0...param_count instead of 0..param_count?
... excludes the ending number. If param_count is 3, 0...3 gives three indexes: 0, 1, and 2.
Does Ruby map modify the original array?
No. map returns a new array. map! is the mutating version for arrays.
What is the difference between map and each in Ruby?
map is for producing transformed values. each is for actions such as printing, logging, or updating external state.
Can I use map on a Range in Ruby?
Yes. Ranges include Enumerable, so you can map each value in a range into a new array.
What does map(&:to_s) mean?
It is a shorthand for calling to_s on each item. It is equivalent to map { |item| item.to_s }.
Mini Project
Description
Build a small Ruby helper that generates parameter names for a dynamic form or API request. This demonstrates how an exclusive range and map can create a predictable array from a count.
Goal
Create a method that returns names such as "param_0", "param_1", and "param_2" for a supplied parameter count.
Requirements
Accept a parameter count as an argument.
Return one generated name for each parameter.
Use an exclusive range so the output contains exactly the requested number of names.
Use map with a block to transform indexes into strings.
Return an empty array when the count is 0.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.