luaguides

ipairs

ipairs() is a stateless iterator for traversing Lua tables that contain sequential, array-style data. It iterates over integer keys in ascending order, starting from 1, and stops at the first nil value. This makes it the right choice when you know your table has no gaps.

Basic Usage

t = {"apple", "banana", "cherry"}

for i, v in ipairs(t) do
    print(i, v)
end
-- 1    apple
-- 2    banana
-- 3    cherry

ipairs vs pairs

The core difference is that ipairs only touches integer keys starting at 1, in order, while pairs iterates all keys (including strings and non-sequential integers) with no guaranteed order.

Behavioripairspairs
Iterates string keysNoYes
Iterates non-sequential integer keysNoYes
Preserves insertion orderYesNo
Stops at first nilYesNo
Skips nil gapsYesNo
t = {1, nil, 3, "hello", [10] = "late"}

-- ipairs stops at the nil gap — never reaches index 3 or beyond
for i, v in ipairs(t) do
    print(i, v)
end
-- 1    1

-- pairs visits everything including the string key and non-sequential integer
for k, v in pairs(t) do
    print(k, v)
end
-- 1         1
-- 3         3
-- hello     world
-- 10        late

Sequential Tables Only

ipairs is designed for tables that represent arrays — consecutive integer keys starting at 1 with no nil gaps. If your table has holes, use pairs instead or fill the gaps.

-- Clean array: works perfectly with ipairs
fruits = {"apple", "banana", "cherry"}

-- Sparse array: ipairs stops early
sparse = {}
sparse[1] = "first"
sparse[3] = "third"  -- index 2 is nil
sparse[5] = "fifth"  -- index 4 is nil

for i, v in ipairs(sparse) do
    print(i, v)
end
-- 1    first
-- 3    third
-- ipairs stops here because index 4 is nil

Index Starting at 1

Lua arrays are 1-indexed. ipairs enforces this by starting at index 1 — if your table starts at index 0 or some other number, ipairs will visit nothing.

-- Zero-indexed table — ipairs visits nothing
zero = {[0] = "zero", [1] = "one", [2] = "two"}

for i, v in ipairs(zero) do
    print(i, v)
end
-- (no output)

-- This is why Lua arrays conventionally start at 1

Practical Patterns

Iterating With Default Values

A common pattern fills nil slots with a default so ipairs works reliably:

t = {1, nil, 3, nil, 5}

-- Fill holes with a default value
for i, v in ipairs(t) do
    print(i, v)
end
-- 1    1
-- (stops at index 2 because it's nil)

-- Safer: pre-populate
safe = {1, 0, 3, 0, 5}
for i, v in ipairs(safe) do
    print(i, v)
end
-- 1    1
-- 2    0
-- 3    3
-- 4    0
-- 5    5

Building an Array

ipairs naturally handles tables built incrementally:

local result = {}
for i = 1, 10 do
    result[i] = i * i
end

for i, v in ipairs(result) do
    print(i, v)
end
-- 1    1
-- 2    4
-- ...
-- 10   100

Skipping the Index

If you only need values, ignore the index:

for _, v in ipairs({"a", "b", "c"}) do
    print(v)
end
-- a
-- b
-- c

Common Mistakes

Assuming ipairs Visits All Integer Keys

ipairs only visits keys 1, 2, 3… in order. It never touches index 0 or any non-sequential integer keys.

t = {[1] = "first", [3] = "third", [5] = "fifth"}

for i, v in ipairs(t) do
    print(i, v)
end
-- 1    first
-- (stops because 2 is nil)

Using ipairs on Hash Tables

If your table uses string keys or has non-sequential integer keys, pairs is the correct choice:

-- Configuration-style table (hash map)
config = {host = "localhost", port = 8080, max = 100}

-- ipairs visits nothing on a hash-only table
for i, v in ipairs(config) do
    print(i, v)
end
-- (no output)

-- Use pairs instead
for k, v in pairs(config) do
    print(k, v)
end
-- host      localhost
-- port      8080
-- max       100

See Also