Skip to content

4. Tables and Loops

Learning Objectives

  • be familiar with Lua tables
  • understand the uses of metatables and metamethods
  • understand how to use while and for loops

Tables as Associative Arrays

Tables in Lua are neither values nor variables but objects. The table type implements associative arrays, which are arrays that can be indexed by any value except nil.

Tables do not have a fixed size and can be changed dynamically. We use tables to represent ordinary arrays, dictionaries, sets, queues and other data structures. All tables are created with the {} constructor expression. Values are accessed and assigned with the [] operator.

-- creating a table of 3 elements indexed by 1,2
local a = {}
a[1] = "apples"
a[2] = "bananas"

-- accessing the 2nd element
print(a[2]) -- output: "bananas"

Common mistake

Unlike in other languages, accessing unset indexes returns nil instead of throwing errors

local a = {}
print(a[1000])  -- output: "nil"

Arrays

Tables can be used as arrays simply by adding in values into the constructor. Note that tables in Lua are by default 1-indexed.

-- using constructors to create and initialize arrays in a single expression
local squares = {1, 4, 9, 16, 25, 36, 49, 64, 81}

Changing index

We can also start an array at index 0, 1, or any other value by just manually setting indexes:

-- starts at -5 and increments by 1 each index
local squares = {[-5] = 1, 4, 9, 16, 25, 36, 49, 64, 81}

However, we recommend always starting arrays with index 1 to allow the Lua standard library functions to work properly.

Dictionaries

Tables can also be used as dictionaries, allowing you to associate keys with values. These keys can be strings, numbers, or any other type except nil

-- using constructors to create and initialize dictionaries in a single expression
local shoppingList = {
    ["apples"] = 3,
    ["oranges"] = 4
}

If the keys are valid Lua identifiers (i.e. strings that are alphanumeric without spaces or special characters), we can omit the [] during table creation:

local shoppingListTwo = {
    apples = 3,
    oranges = 4
}

Dot Syntax

Dictionaries with Lua identifier keys can be also be quickly indexed by the dot syntax as shorthand for accessing or modifying the table.

-- creating a table with string keys
local person = {name = "Alice", age = 25}
print(person.name)  -- output: "Alice"
person.age = 26     -- modifying person["age"]

Metatables

Tables in Lua can have a special feature called metatables, which allow you to define custom behaviors for operations performed on the table. Metatables enable powerful features like operator overloading, handling undefined keys, and more.

Setting a metatable

You can assign a metatable to a table using the setmetatable library function:

local t = {}
local mt = {}
setmetatable(t, mt)

Metamethods

Metatables can define metamethods - special fields that control the behaviour of a table. These are similar to dunder methods in Python. Common metamethods include:

  • __index (define what happens when accessing a nonexistent key)
  • __newindex (define what happens when assigning to a nonexistent key)
  • __add, __sub, __mul, etc. (define how operators behave with the table)

For instance, here we use __index to provide a default value:

local point = {}
local defaults = {x = 0, y = 0}
local mt = {__index = defaults}
setmetatable(point, mt)

print(point.x, point.y) -- output: "0 0"
point.x = 10
print(point.x, point.y) -- output: "10 0"

We will later cover how to use metatables and metamethods to simulate classes and OOP-like behaviour in Lua.

for loops

for loops are used to iterate over a finite sequence of numbers or values.

Numeric for loop

Numeric for loop is used to iterate over a sequence of numbers, and it includes a start value, stop value, and optional step value (the increment per iteration)

for variable = start, stop, step do
    -- loop body
end

Numerically iterating over arrays

We can use the numeric for loop to iterate over arrays, similar to how it would be done in other programming languages like so:

for i in 1, #t do   -- #t returns the size of table t
    local v = t[i]  -- getting value at index i
    -- loop body
end

ipairs

ipairs is a standard library function which helps to iterate over a table until the first nil is found. We typically use it to traverse continuous arrays. It is the idiomatic way to traverse arrays in Lua.

-- iterate over every element in the array
for index, value in ipairs(t) do
    -- loop body
end

ipairs

pairs is a standard library function which helps to iterate over all key-value pairs in a table. We typically use it to traverse dictionaries.

-- iterate over every key-value pair in the dictionary
for key, value in pairs(t) do
    -- loop body
end

Generic for loops

Luau also has generic for loop syntactic sugar which automatically determines which standard library function, ipairs or pairs, to use when dealing with a table. It is the idiomatic way to traverse tables in Roblox.

for x, value in t do -- x might be a key or index depending on the type of table
    -- loop body
end

while loops

while loops are used when you want to repeat execution of the loop body for as long as the condition stays true.

while condition do
    -- loop  body
end

repeat-until loops

repeat-until loop is similar to a standard while loop except that that the loop body is guaranteed to be executed at least once, and repeated until the condition is reached

repeat
    -- loop body
until condition