Iterators: pairs, ipairs and generic for

Lua · Practical · 10 min

The generic for is how you traverse every table and stream in Lua.

The generic for loop, for k, v in explist do ... end, calls an iterator function repeatedly until it returns nil.

  • ipairs(t) walks t[1], t[2], ... in order and stops at the first nil. Use it for arrays.
  • pairs(t) visits every key in the table, in an unspecified order. Use it for dictionaries, and never rely on the order. To iterate in a fixed order, collect the keys, table.sort them, then loop.

You must not add new keys to a table while traversing it with pairs (assigning to existing fields, including setting them to nil, is allowed).

Writing your own iterator is easy with a closure: return a function that produces the next value each call and nil when done. Under the hood the generic for accepts three values (an iterator function, an invariant state and a control value), which is how pairs and ipairs avoid allocating closures. Lua 5.4 adds a fourth closing value used with to-be-closed variables.

Iterators make lazy pipelines natural: io.lines(path) returns an iterator over a file's lines and s:gmatch one over pattern matches, so you can process large inputs without building a table first.

Example

local scores = {ana = 90, bo = 72, cy = 85}

local names = {}
for name in pairs(scores) do names[#names + 1] = name end
table.sort(names)                      -- fixed order
for i, name in ipairs(names) do
  print(i, name, scores[name])
end
--> 1	ana	90
--> 2	bo	72
--> 3	cy	85

local function range(n, step)
  local i = 0
  return function()                    -- closure iterator
    i = i + (step or 1)
    if i <= n then return i end
  end
end
for x in range(10, 4) do io.write(x, " ") end  --> 4 8
print()

for i, v in ipairs({1, 2, nil, 4}) do io.write(v, " ") end --> 1 2
print()

Remember

  • ipairs for arrays (stops at the first nil), pairs for all keys
  • pairs order is unspecified; sort keys when order matters
  • An iterator is just a function that returns nil when finished

Watch out for

  • Adding keys to a table while iterating it with pairs, which is undefined behaviour.

Check yourself

  1. for i, v in ipairs({1, 2, nil, 4}) prints how many elements?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.lua.org/manual/5.4/manual.html#3.3.5