Question

How to Map with Index in Ruby

rubyarraysindexing

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_index to 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:

  • x1
  • x2
  • x3

You see:

  • x1 at position 2
  • x2 at position 3
  • x3 at position 4

Then you package each result as a pair:

[element, index]

That is exactly what map.with_index(2) does for you.

Take Quiz

Syntax and Examples

Core syntax

array.map.with_index(start) { |element, index| ... }
  • array is the original collection
  • map creates a new array
  • with_index(start) provides the index, beginning from start
  • 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 index 2
  • "x2" gets index 3
  • "x3" gets index 4

Another example: default index starting at 0

items = ["a", "b", "c"]
result = items.map.with_index { |item, i| [item, i] }

p result
# => [["a", 0], ["b", 1], ["c", 2]]

If you need a different starting point, pass it explicitly.

Equivalent approach using manual offset

items = ["x1", "x2", "x3"]
result = items.map.with_index { |item, i| [item, i + 2] }

p result
# => [["x1", 2], ["x2", 3], ["x3", 4]]

This is also valid, but with_index(2) is usually clearer when the offset is simply a different starting number.

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:

  1. items is assigned the array:

    ["x1", "x2", "x3"]
    
  2. map starts iterating over the array.

  3. with_index(2) tells Ruby:

    • include an index value
    • start counting from 2
  4. First iteration:

    • item = "x1"
    • i = 2
    • block returns:
    ["x1", 2]
    
  5. Second iteration:

    • item = "x2"
    • i = 3
    • block returns:
    ["x2", 3]
    
  6. Third iteration:

    • item = "x3"
    • i = 4
    • block returns:
    ["x3", 4]
    
  7. map collects all returned values into a new array:

[["x1", 2], ["x2", 3], ["x3", 4]]

The original array is unchanged.

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(100) { |name, id| { id: id, name: name } }
# => [{ id: 100, name: "book" }, { id: 101, name: "pen" }]

Useful when converting plain data into structured records.

Take Quiz

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.

result = items.map.with_index(1) { |item, i| [i, item] }

5. Prefer each_with_index when you only want side effects

If you are printing, logging, or mutating something else, each_with_index may be a better fit.

items.each_with_index do |item, i|
  puts "#{i}: #{item}"
end

6. Keep blocks simple

If the mapping logic becomes hard to read, extract it.

def pair_with_position(item, position)
  [item, position]
end

result = items.map.with_index(2) { |item, i| pair_with_position(item, i) }

This keeps production code clearer and easier to test.

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_index iterates, 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] }

Mistake 3: Mixing up block parameter order

Broken code:

items.map.with_index(2) { |i, item| [item, i] }

Problem:

  • the first block parameter is the element
  • the second is the index

Correct version:

items.map.with_index(2) { |item, i| [item, i] }

Mistake 4: Using map.with_index on something you do not intend to transform

If you only need to print or log values, map is misleading.

Less ideal:

items.map.with_index { |item, i| puts "#{i}: #{item}" }

Better:

items.each_with_index { |item, i| puts "#{i}: #{item}" }

Mistake 5: Forgetting that map returns a new array

items = ["x1", "x2", "x3"]
items.map.with_index(2) { |item, i| [item, i] }

p items
# => ["x1", "x2", "x3"]

The original array does not change unless you assign the result:

items = items.map.with_index(2) { |item, i| [item, i] }

Comparisons

ApproachUse whenReturnsExample
map.with_indexYou want a new transformed array and need the indexNew array`items.map.with_index(2) {
each_with_indexYou want to iterate with index for side effectsOriginal collection`items.each_with_index {
map onlyYou want a new array but do not need indexNew array`items.map {
each onlyYou want side effects and do not need indexOriginal collection`items.each {

with_index(2) vs i + 2

StyleExampleNotes
Start index directlymap.with_index(2)Clear and expressive
Add offset manually`map.with_index {item, i

In simple cases like this question, map.with_index(2) is usually the cleanest choice.

Take Quiz

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

Parameter order

|element, index|

Not:

|index, element|

Original array

map does not modify the original array unless you assign the result back.

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 with_index(1) or with_index(2) if you need a different start.

What is the simplest solution for converting [x1, x2, x3] to [[x1, 2], [x2, 3], [x3, 4]] in Ruby?

array.map.with_index(2) { |x, i| [x, i] }
Take Quiz

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.
Take Quiz

Keep learning

Related questions

View more