22. Tables as lists

A table is Lua's one and only built-in data structure. Depending on how you use it, the same table can hold a list of values, a dictionary of key-value pairs, or both. This chapter covers the list shape; the next, the dictionary.

Creating a list

A list is a table with values stored under keys 1, 2, 3, and so on. The easiest way to make one is with curly braces:

local fruits = {"apple", "banana", "cherry"}
print(fruits[1])   -- apple
print(fruits[2])   -- banana
print(fruits[3])   -- cherry

Two surprises if you have seen other languages:

  • Indexing starts at 1, not 0. The first item is at [1]. Roblox follows the same convention.
  • Square brackets [...] look up a value; the number inside is the index.

The variable fruits holds a reference to the table. The braced values become the items at positions 1, 2, 3.

Length with #

The same # you used for strings works on lists. It returns the item count:

local fruits = {"apple", "banana", "cherry"}
print(#fruits)   -- 3
print(fruits[#fruits])   -- cherry  (the last one)

fruits[#fruits] reads the value at the position equal to the length — the last one. A common pattern.

Looping over a list

Two natural ways to walk through a list.

Numeric for

If you only need the items, a numeric for from 1 to #t works:

for i = 1, #fruits do
    print(i, fruits[i])
end

Output:

1   apple
2   banana
3   cherry

ipairs

ipairs is a built-in iterator that yields the index and value in order, starting at 1:

for index, value in ipairs(fruits) do
    print(index, value)
end

Same output. ipairs is more idiomatic for lists because it makes the intent — iterating in order — obvious.

Open exercises/22/01-list-loop.lua. Add a fourth fruit and re-run. Both #fruits and the loop should adjust automatically.

Adding and removing items

Two ways to append to a list:

local fruits = {"apple", "banana"}

table.insert(fruits, "cherry")    -- appends to the end
fruits[#fruits + 1] = "date"      -- also appends; manual version

print(fruits[3])   -- cherry
print(fruits[4])   -- date

table.insert(t, pos, value) inserts at pos and shifts later items right:

table.insert(fruits, 1, "apricot")  -- new first item
print(fruits[1])   -- apricot
print(fruits[2])   -- apple  (shifted from 1 to 2)

table.remove(t, pos) removes the value at pos and shifts later items left. With no pos, it removes the last:

table.remove(fruits, 1)   -- drops "apricot"
table.remove(fruits)      -- drops the new last item

Setting a slot to nil (fruits[2] = nil) does not shift later items; it leaves a hole in the list. After that, #fruits and ipairs may stop counting at the hole. Use table.remove to drop a value cleanly.

Tables are passed by reference

Tables behave differently from numbers and strings in one important way: assigning a table to another variable does not make a copy.

local a = {1, 2, 3}
local b = a       -- b refers to the SAME table
b[1] = 99
print(a[1])       -- 99   (the change shows through a too)

Both a and b name one table. For a real copy you must loop and build a new table — there is no built-in table.copy. You rarely need to.

Homework

Problem 1 — Favourite games

Open exercises/22/homework/01-favourite-games.lua. Create a list of five game names. With a for ... in ipairs(...) do loop, print each as 1. Roblox, 2. Minecraft, and so on.

Problem 2 — Sum the list

Open exercises/22/homework/02-sum-list.lua. A list of numbers is declared at the top. Compute and print its sum.

Problem 3 — Insert and remove

Open exercises/22/homework/03-insert-remove.lua. The starter has a short list. Do four things, printing the full list after each step:

  1. Append a new item.
  2. Insert another item at position 1.
  3. Remove the last item.
  4. Remove the item at position 2.

Challenge — Reverse a list

Open exercises/22/homework/04-reverse.lua. Define a local function reverse(list) that returns a new list with the items reversed, leaving the original unchanged. Test it on a list of strings and print both before and after.

Stuck or finished? Open the homework solutions page.