Iterating Lists


Iterating over the elements of a list is a very common operation.

Known Length Lists

If the number of elements in the list is known, then it is easy to iterate over the elements using a simple numeric for loop:

local x = { "a", "b", "c", "d" }

for i = 1, 4 do
    print(i .. ": " .. x[i])
end

which produces the result:

1: a

2: b

3: c

4: d

This can also be used to iterate over list that contain nil values, which (as we will see in a moment) can be problematic when iterating over lists using other methods. Note that because the concatenation operator will not allow a nil value we have to test for nil and replace it with a string "nil":

local x = { "a", "b", nil, "c", "d" }

for i = 1, 5 do
    print(i .. ": " .. (x[i] or "nil"))
end

which produces the output:

1: a

2: b

3: nil

4: c

5: d

Unknown Length Lists

When the length of the list is not known we can use the generic for to iterator over the items in the list. There are two built-in iterator functions, pairs and ipairs. Let's first look at iterating lists using pairs:

pairs

The pairs function returns an iterator over key, value pairs from the list. Due to the way that lists are created with tables, the keys are the indices of each element of the list, while the values are the element values themselves. Let's take a look:

local x = { "a", "b", "c", "d" }

for i, v in pairs(x) do
    print(i .. ": " .. v)
end

which produces the output:

1: a

2: b

3: c

4: d

Let's compare that to the other built-in iterator function, ipairs:

ipairs

The ipairs iterator function creates a counter and iterates over the elements of the list, yielding index, value pairs. Due to the way that lists are created with tables, this produces the same output that we just saw with pairs:

local x = { "a", "b", "c", "d" }

for i, v in ipairs(x) do
    print(i .. ": " .. v)
end

with the output:

1: a

2: b

3: c

4: d

While both iterator functions produced the same output, they behave quite differently when there are nil values in the lists:

Careful!

Be careful when there are nil values in the list, as the results can be a bit unexpected and surprising.

using pairs with nil values

Let's first look at how the pairs function behaves when there are nil values in the list:

local x = { "a", "b", nil, "c", "d" }

for i, v in pairs(x) do
    print(i .. ": " .. v)
end

with the result:

1: a

2: b

4: c

5: d

Although there are five elements in the list, the pairs function only yields four of them, silently skipping the nil element. While this can be a helpful feature, in some cases we will want to know if there are nil values in the list and this behavior would prevent that.

Now let's see how the ipairs function behaves:

using ipairs with nil values

Here we look at the same input again, but with the ipairs function:

local x = { "a", "b", nil, "c", "d" }

for i, v in ipairs(x) do
    print(i .. ": " .. v)
end

which produces somewhat different results:

1: a

2: b

Rather than iterating over each item in the list, the ipairs function simply stopped iterating elements when it reached the first nil value. This is rarely expected behavior, and can lead to some unexpected behavior.

We can learn more about this behavior by examining how the length operator behaves with lists, in the next section.