In pairs() vs in ipairs() Which one should I use?

If you want to run a for loop, it would look a little something like this: for i, v in pairs(table) do end, But when should I use ipairs() rather than pairs() in a for loop?

9 Likes

The difference is that ipairs() will not go through nil values and stop the for loop after that. As tested in the code sample below.

local t = {"a", nil, "c"}

for i, v in ipairs(t) do
    print(i, v) -- 1 a only
end
15 Likes

ipairs will also iterate through the passed array in order. The order that pairs iterates in is undefined.

ipairs can be written in Lua as:

local function inext(tab, index)
	index = index + 1
	local value = tab[index]
	if value ~= nil then
		return index, value
	end
end

local function ipairs(tab)
	return inext, tab, 0
end

pairs can be written in Lua as:

local function pairs(tab)
	return next, tab, nil
end

next is a primitive function in Lua that takes a table and key and returns the next key-value pair of that table.

17 Likes

ipairs() will only work on numerical indexes, it’s mainly built for arrays though however I’d recommend using a numerical for loop over a generic due to the micro-optomisation.

3 Likes

Undefined according to the doc, but the behavior in Lua 5.1.4 is to iterate the array part of the table in order, you don’t have to go back and change your pairs code worrying that it’s subtly broken somehow if you do use pairs expecting the array part to be in order.

5 Likes

The only time it makes sense to use ipairs is if you have a mixed table and need to scan through the numerical indices. However, I would argue that mixed tables can be avoided & are bad design in most cases.

2 Likes

That’s not technically correct though. If you’re following the Lua docs word for word, then if you care about the order of iteration you have to use ipairs, even if the table isn’t mixed. Thanks to: Lua 5.1 Reference Manual

The order in which the indices are enumerated is not specified, even for numeric indices . (To traverse a table in numeric order, use a numerical for or the ipairs function.)

2 Likes

I’ve found some obscure cases where pairs does not iterate over a table with ordered gapless integer keys in order but ipairs (as it never actually cares about the array or dictionary part of the table) does:

local array = {[1] = "a", [2] = "b", [3] = "c", [4] = "d"}
for key, value in pairs(array) do
    print(key, value) --> unordered
end
for key, value in ipairs(array) do
    print(key, value) --> ordered
end

Is this because the table is constructed in such a way (with the dictionary style) that some (or all) key-value pairs are not added to the array part of the table? This seems like the case as refreshing(?) the table by inserting a new pair makes pairs iterate in order:

array[5] = "e"
for key, value in pairs(array) do
    print(key, value) --> ordered
end
3 Likes

I would like to point out that in Roblox’s implementation of Lua, ipairs is very slightly slower than pairs. That shouldn’t be the case, but unless something has changed in the past year or two, it is. If you care about efficiency, you should use pairs for dictionaries and a standard numeric for loop (for i = 1,#tab) for lists.

2 Likes
4 Likes

It works that way because using the [key] = value syntax, you’re directly inserting into the hash part of the table. Lua doesn’t do any special extra check in that case to see whether the key you’re inserting is actually an integer which could go into the array part, it’s just inserting an arbitrary key into the hash part.

As long as you only ever use array operations (table.insert / remove, or manually inserting at the end), then it will preserve ordered behavior.

3 Likes

Well then I’ve got a lot of code to fix :slight_smile:

1 Like

Thank you. I just finished updating Studio and I can verify that it still happens, however.

local t = {} for i=1,100000000 do t[i]=i end local s=tick() for i,v in ipairs(t) do end local e = tick() print(e-s)

6.904 ipairs

local t = {} for i=1,100000000 do t[i]=i end local s=tick() for i,v in pairs(t) do end local e = tick() print(e-s)

1.388 pairs

local t = {} for i=1,100000000 do t[i]=i end local s=tick() for i = 1,#t do end local e = tick() print(e-s)

1.180 numeric loop

1 Like

Have you tried this in a live game? I’m fairly certain ipairs had been greatly boosted.

1 Like

I feel like this is something you can ignore. Realistically you are never really going to iterate over big tables in this mannerism. As well, ipairs saw a significant speed increase with Luau; the speed increase benchmarks as noted in RDC had around a 4 for pairs and 6 for ipairs.

I encourage you to try other tests if you’re worrying about speed (which you really shouldn’t be at all). Smaller tables, more practical scenarios, so on.

3 Likes

Ran your tests again, and they seem to be fairly consistent within a certain margin of error.

The ipairs loop returned:
7.2337448596954

The pairs loop returned:
0.90113949775696

The numeric loop returned:
0.51699042320251 (I actually tested this one a couple times, though thats because of how fast it was, it was always within a margin of .1)

However running these functions almost made my studio crash, so i didn’t test them beyond one time. Interesting thing to note here is that the numeric loop was twice as fast for me as it appeared to have been for you.

Again i do think its important to note based on what was previously said by others, this is a very fringe scenario, the performance difference might be negligible in normal workloads.

2 Likes