107

In a for loop, what is the difference between looping with pairs() versus ipairs()?

The Programming in Lua book mentions both, however, both appear to generate similar outputs as shown below:

Using ipairs():

a = {"one", "two", "three"}
for i, v in ipairs(a) do
  print(i, v)
end

Output:

1   one
2   two
3   three

Using pairs():

a = {"one", "two", "three"}
for i, v in pairs(a) do
  print(i, v)
end

Output:

1   one
2   two
3   three
Kyle F Hartzenberg
  • 2,567
  • 3
  • 6
  • 24

2 Answers2

167

ipairs() and pairs() are slightly different, as you can see on the manual reference. A less-technical description could be that:

  • ipairs() returns index-value pairs and is mostly used for numeric tables. The non-numeric keys are ignored as a whole, similar to numeric indices less than 1. In addition, gaps in between the indexes lead to halts. The ordering is deterministic, by numeric magnitude.

  • pairs() returns key-value pairs and is mostly used for associative tables. All keys are preserved, but the order is unspecified.

In addition, while pairs() may be used to get the size of a table (see this other question), using ipairs() for the same task is unsafe a priori, since it might miss some keys.

The differences between both options is illustrated in the following code fragment.

> u = {}
> u[-1] = "y"
> u[0] = "z"
> u[1] = "a"
> u[3] = "b"
> u[2] = "c"
> u[4] = "d"
> u[6] = "e"
> u["hello"] = "world"
>
> for key, value in ipairs(u) do print(key, value) end
1       a
2       c
3       b
4       d
>
> for key, value in pairs(u) do print(key, value) end
1       a
2       c
3       b
4       d
6       e
0       z
hello   world
-1      y
> 

As we can see in the example, while all keys appear in the output for pairs(), some are missing for ipairs(): hello because it is not a numeric key; -1 and 0 since, despite they are numeric, their value is less than 1, and; 6 given that we implicitly have u[5] = nil, and finding a nil value while iterating is exactly the ending condition for ipairs().

Finally, note that as in your example, when you create a table without specifing any of the keys, e.g., a = {"one", "two", "three"}, numeric keys starting in 1 are implicitly assigned to each of the values, i.e. the definition is understood as a = { [1] = "one", [2] = "two", [3] = "three" }. As a consequence, using pairs() or ipairs() is mostly the same in these scenarios, except for the non-guaranteed ordering of pairs().

Alain Merigot
  • 10,667
  • 3
  • 18
  • 31
  • I believe that the ordering is guaranteed with pairs(), and it will go up through the numeric indexes and then will go to the other non numeric values in a random order. –  Mar 12 '19 at 10:39
  • 4
    @BeastCoder2: At https://www.lua.org/manual/5.3/manual.html#pdf-next, it says "The order in which the indices are enumerated is not specified, *even for numeric indices*." What you describe might be common behavior in Lua, but it could change in any bugfix release. – luther Mar 12 '19 at 17:19
  • @luther Thank you for clearing that up! It may just have been what I was using to run the code. –  Mar 12 '19 at 19:03
  • 1
    I've also seen `pairs` iterating over consecutive integer indices beginning at 1 (and sometimes over non-consecutive integer indices) in order, but it's not guaranteed. – cyclaminist Mar 12 '19 at 21:17
  • 23
    Another important point about `ipairs` is that it only loops over the first *sequence* of indices, starting at 1: it stops at the first gap. Consider `for i,v in ipairs({[1]='a', [2]='b', [4]='d'}) do print(i,v) end` and `for i,v in ipairs({[2]='a', [3]='b', [4]='d'}) do print(i,v) end` – glenn jackman Mar 13 '19 at 14:21
  • 1
    @glennjackman I thought of the same detail when I read the answer, though I also did not know about the negative and zero indexes being omitted, so thanks for that. I think this comment is not a minor one, hence I have updated the answer to reflect this particular behaviour of ipairs(). – Julian Martin Del Fiore Jan 15 '23 at 09:52
5

There is no array-type in Lua, only tables which might have consecutive elements starting from index 1.

The generic for-loop, in contrast to the numeric for-loop, expects three values:

  1. A callable
  2. A context-value it passes on
  3. An initial index-value

It calls the callable with context-value and index-value, storing all the returned values in the provided new variables. The first one is additionally saved as the new index-value.

Now some representative examples of callables for the loop:

  1. ipairs(t) returns a function, the table t, and the starting-point 0.
    The function is the moral equivalent to:

    function ipairs_next(t, i)
        i = i + 1
        var v = t[i]
        if v ~= nil then
            return i, v
        end
    end
    

    Thus, all numeric entries starting at 1 until the first missing one are shown.

  2. pairs(t) either delegates to t's metatable, specifically to __pairs(t), or returns the function next, the table t, and the starting-point nil. next accepts a table and an index, and returns the next index and the associated value, if it exists.

    Thus, all elements are shown in some arbitrary order.

  3. There are no limits to how creative one can be with the function, and that is what vanilla Lua expects.
    See "Bizzare "attempt to call a table value" in Lua" for an example of a user-written callable, and how some dialects react if the first value is not actually a callable.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • I’m new to the language and was wondering what each one is, what is each type of for loop? What is the callable, context-value, and index-value? What is ipairs_next, a function you made? –  Mar 11 '19 at 19:53
  • i am new to lua too but i think that it is an iterator function, lookup "iterators in programming" – Karambit Jan 21 '21 at 19:12