Question
How can I transform a Ruby array like this:
[x1, x2, x3, ..., xN]
into this form:
[[x1, 2], [x2, 3], [x3, 4], ..., [xN, N + 1]]
In other words, I want to collect each element together with an index-based value, where the first element is paired with 2, the second with 3, and so on. What is the simplest Ruby way to do this?
Short Answer
By the end of this page, you will understand how to transform arrays while also using each element’s position in Ruby. You will learn the most common Ruby patterns for this task, especially map.with_index, how to shift the starting index, and how to choose between map, each_with_index, and similar approaches.
Concept
In Ruby, map is used when you want to transform every element of a collection into a new value. Sometimes, the transformation depends not only on the element itself, but also on its position in the array.
That is where indexed iteration becomes useful.
Ruby gives you a clean way to combine transformation and indexing:
array.map.with_index
This lets you access both:
- the current element
- the current index
Because Ruby array indexes normally start at 0, you can either:
- add an offset manually, or
- tell
with_indexto start counting from a different number
For your example, you want the first element paired with 2, the second with 3, and so on. That means you want the index sequence to begin at 2 instead of 0.
This matters in real programming because many data transformations depend on position:
- numbering rows for export
- creating labels like
item_1,item_2 - building coordinate pairs
- formatting data for APIs
- attaching line numbers to text
Understanding how to map with an index helps you write Ruby code that is both concise and expressive.
Mental Model
Think of map like a factory conveyor belt.
- Each item moves past you one at a time.
- You replace each original item with a new version.
- Sometimes you also need the item’s position on the belt.
with_index is like adding a counter next to the belt.
So instead of seeing only:
x1x2x3
You see:
x1at position2x2at position3x3at position4
Then you package each result as a pair:
[element, index]
That is exactly what map.with_index(2) does for you.
Syntax and Examples
Core syntax
array.map.with_index(start) { |element, index| ... }
arrayis the original collectionmapcreates a new arraywith_index(start)provides the index, beginning fromstart- the block returns the transformed value for each element
Example for this question
items = ["x1", "x2", "x3"]
result = items.map.with_index(2) { |item, i| [item, i] }
p result
# => [["x1", 2], ["x2", 3], ["x3", 4]]
This works because:
"x1"gets index2"x2"gets index3"x3"gets index4
Another example: default index starting at 0
items = [, , ]
result = items.map.with_index { || [item, i] }
p result
Step by Step Execution
Consider this code:
items = ["x1", "x2", "x3"]
result = items.map.with_index(2) { |item, i| [item, i] }
Here is what happens step by step:
-
itemsis assigned the array:["x1", "x2", "x3"] -
mapstarts iterating over the array. -
with_index(2)tells Ruby:- include an index value
- start counting from
2
-
First iteration:
item = "x1"i = 2- block returns:
["x1", 2] -
Second iteration:
item = "x2"
Real World Use Cases
Numbering exported rows
rows = ["Alice", "Bob", "Cara"]
numbered = rows.map.with_index(1) { |name, line| [line, name] }
# => [[1, "Alice"], [2, "Bob"], [3, "Cara"]]
Useful for CSV exports, reports, or invoices.
Building labels for UI data
fields = ["email", "password", "phone"]
labeled = fields.map.with_index(1) { |field, i| [field, "field_#{i}"] }
# => [["email", "field_1"], ["password", "field_2"], ["phone", "field_3"]]
Useful when generating form metadata.
Attaching line numbers to file content
lines = ["first line", "second line"]
numbered = lines.map.with_index(1) { |text, line_no| [line_no, text] }
# => [[1, "first line"], [2, "second line"]]
Useful in parsers, editors, or debugging tools.
Creating structured API payloads
products = ["book", "pen"]
payload = products.map.with_index() { || { id, name } }
Real Codebase Usage
In real Ruby projects, developers often use indexed mapping in a few common ways.
1. Transforming data for output
users.map.with_index(1) do |user, position|
{ rank: position, name: user.name }
end
This is common in serializers, presenters, and report generation.
2. Adding stable display numbers
errors.map.with_index(1) do |message, i|
"#{i}. #{message}"
end
Good for validation summaries or command-line output.
3. Building derived values from both content and position
columns.map.with_index do |column, i|
"col_#{i}_#{column}"
end
Useful in import/export tools and dynamic SQL helpers.
4. Prefer map when you want a new array
If your goal is transformation, use map.
Common Mistakes
Mistake 1: Using each when you want a transformed array
Broken idea:
items = ["x1", "x2", "x3"]
result = items.each_with_index { |item, i| [item, i + 2] }
p result
# => ["x1", "x2", "x3"]
Why it happens:
each_with_indexiterates, but it returns the original collection- it does not collect the block results into a new array
Use this instead:
result = items.map.with_index(2) { |item, i| [item, i] }
Mistake 2: Forgetting that Ruby indexes start at 0
Broken expectation:
items = ["x1", "x2", "x3"]
result = items.map.with_index { |item, i| [item, i] }
p result
# => [["x1", 0], ["x2", 1], ["x3", 2]]
If you need counting from 2, use:
items.map.with_index(2) { |item, i| [item, i] }
Comparisons
| Approach | Use when | Returns | Example |
|---|---|---|---|
map.with_index | You want a new transformed array and need the index | New array | `items.map.with_index(2) { |
each_with_index | You want to iterate with index for side effects | Original collection | `items.each_with_index { |
map only | You want a new array but do not need index | New array | `items.map { |
each only | You want side effects and do not need index | Original collection | `items.each { |
with_index(2) vs i + 2
Cheat Sheet
Quick reference
Transform array with index
array.map.with_index { |element, index| ... }
Start index from a custom number
array.map.with_index(2) { |element, index| ... }
Example
["x1", "x2", "x3"].map.with_index(2) { |item, i| [item, i] }
# => [["x1", 2], ["x2", 3], ["x3", 4]]
Default index start
- Ruby indexes normally start at
0
Use map when
- you want a new transformed array
Use each_with_index when
- you want to iterate with index
- you do not need a new transformed array
Common pattern
array.map.with_index(1) { |value, i| [i, value] }
FAQ
How do I map an array with an index in Ruby?
Use map.with_index:
array.map.with_index { |element, index| ... }
How do I start the index at 1 or 2 in Ruby?
Pass the starting value to with_index:
array.map.with_index(2) { |element, index| ... }
What is the difference between map.with_index and each_with_index in Ruby?
map.with_index creates a new array from the block results. each_with_index just iterates and returns the original collection.
Does map.with_index change the original array?
No. It returns a new array. Assign the result if you want to keep it.
Can I use each_with_index.map instead?
You can in some cases, but map.with_index is usually clearer and more idiomatic for transformations.
Why does my index start at 0 in Ruby?
Ruby arrays are zero-based by default. Use or if you need a different start.
Mini Project
Description
Build a small Ruby script that takes a list of task names and pairs each task with a display number starting from 1. This demonstrates how to transform an array into a new structure while using the position of each element. It is similar to generating numbered menus, to-do lists, or export rows.
Goal
Create a numbered task list where each task becomes a pair of [task_name, number].
Requirements
- Create an array of at least three task names.
- Use Ruby to transform the array into a new array.
- Include a number for each task starting from
1. - Print the final transformed array.
- Do not manually write the output array by hand.
Keep learning
Related questions
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.
Difference Between require and include in Ruby
Learn the difference between require and include in Ruby, when to load a file, and when to mix module methods into a class.
Fixing Ruby Gem Native Extension Errors: mkmf.rb Can't Find Header Files for Ruby
Learn why Ruby gem installs fail with missing ruby.h, how native extensions work, and how to fix header file errors on Linux servers.