Skip to content

Lua Tables

Tables are the sole data structuring mechanism in Lua, and they are fundamental to the language. A table is a highly versatile associative array. This means it can be indexed not only by numbers but also by other Lua values like strings, booleans, or even other tables (except nil). Tables are used to represent ordinary arrays, lists, symbol tables (dictionaries/maps), objects, packages, and more.

Tables in Lua are dynamic; their size is not fixed and can grow or shrink as needed. They are often referred to as objects, but it’s important to understand they are distinct from variables that might refer to them.

An empty table is created using the constructor expression {}.

-- Create an empty table
local my_table = {}
-- Assign values using keys (indices)
my_table[1] = "Apple" -- Numeric key (array-like)
my_table["color"] = "Red" -- String key (dictionary-like)
my_table[true] = "Is a fruit" -- Boolean key
-- Access values using keys
print(my_table[1]) -- Output: Apple
print(my_table["color"]) -- Output: Red
-- Alternative syntax for string keys (if valid identifiers)
my_table.shape = "Round"
print(my_table.shape) -- Output: Round
-- Initialize a table with elements
local another_table = {
name = "Banana",
color = "Yellow",
peel_status = "Peeled",
10, -- equivalent to [1] = 10
20 -- equivalent to [2] = 20
}
print(another_table.name) -- Output: Banana
print(another_table[1]) -- Output: 10

Variables in Lua do not hold tables themselves, but rather references to tables. When you assign one table variable to another, both variables refer to the same table in memory. Modifying the table through one variable affects the other.

local t1 = { value = 10 }
local t2 = t1 -- t2 now refers to the same table as t1
t2.value = 20
print(t1.value) -- Output: 20 (since t1 and t2 point to the same table)
t1 = nil -- t1 no longer refers to the table
-- The table itself still exists because t2 refers to it.
print(t2.value) -- Output: 20
t2 = nil -- Now, no variable refers to the table.
-- Lua's garbage collector will eventually reclaim the memory used by the table.

Example demonstrating table types and references:

-- Simple empty table
local mytable = {}
print("Type of mytable is: " .. type(mytable)) -- Type of mytable is: table
mytable[1] = "Lua"
mytable["feature"] = "Tables"
print("mytable[1] is: " .. mytable[1]) -- mytable[1] is: Lua
print("mytable.feature is: " .. mytable.feature) -- mytable.feature is: Tables
-- alternatetable and mytable refer to the same table
local alternatetable = mytable
print("alternatetable[1] is: " .. alternatetable[1]) -- alternatetable[1] is: Lua
alternatetable.feature = "Powerful Tables"
print("mytable.feature is now: " .. mytable.feature) -- mytable.feature is now: Powerful Tables
-- Release reference from alternatetable
alternatetable = nil
print("alternatetable is: " .. tostring(alternatetable)) -- alternatetable is: nil
-- mytable is still accessible
print("mytable.feature is still: " .. mytable.feature) -- mytable.feature is still: Powerful Tables
mytable = nil -- Now the table is eligible for garbage collection
print("mytable is: " .. tostring(mytable)) -- mytable is: nil

Lua provides a table library with functions for manipulating tables, especially when used as arrays/lists.

Function SignaturePurpose
table.concat(list [, sep [, i [, j]]])Concatenates the string elements of list (an array-like table) from index i to j. Elements are separated by sep (default is empty string). i defaults to 1, j defaults to #list.
table.insert(list, [pos,] value)Inserts value into list at pos. If pos is omitted, inserts at the end. Shifts subsequent elements.
table.remove(list [, pos])Removes the element at pos from list. If pos is omitted, removes the last element. Returns the removed element. Shifts subsequent elements down.
table.sort(list [, comp])Sorts the elements of list in place. An optional comparator function comp(a,b) can be provided, which should return true if a should come before b.
table.pack(...)(Lua 5.2+) Returns a new table with all arguments stored into keys 1, 2, etc. and a field ‘n’ with the total number of arguments.
table.unpack(list [, i [, j]])(Lua 5.1 unpack, Lua 5.2+ table.unpack) Returns the elements from the given list from index i to j as separate return values. i defaults to 1, j defaults to #list.

Note: table.maxn was deprecated in Lua 5.1 and removed in Lua 5.2. To get the length of a sequence-like table (an array without nil holes starting at index 1), use the length operator #.

Examples of Table Manipulation:

local words = {"Lua", "is", "awesome"}
print("Concatenated (default): " .. table.concat(words)) -- Luaisawesome
print("Concatenated with space: " .. table.concat(words, " ")) -- Lua is awesome
print("Concatenated with dash (subset): " .. table.concat(words, "-", 2, 3)) -- is-awesome

Insert and Remove (table.insert, table.remove, # operator)

Section titled “Insert and Remove (table.insert, table.remove, # operator)”
local fruits = {"banana", "orange", "apple"}
print("Initial #fruits:", #fruits) -- Initial #fruits: 3
-- Insert at the end
table.insert(fruits, "mango")
print("Fruit at index 4:", fruits[4], "(#fruits:", #fruits .. ")") -- Fruit at index 4: mango (#fruits: 4)
-- Insert at a specific position
table.insert(fruits, 2, "grapes")
print("Fruit at index 2:", fruits[2], "(#fruits:", #fruits .. ")") -- Fruit at index 2: grapes (#fruits: 5)
-- fruits is now: {"banana", "grapes", "orange", "apple", "mango"}
local removed_fruit = table.remove(fruits) -- Removes last element ("mango")
print("Removed last fruit:", removed_fruit, "(#fruits:", #fruits .. ")") -- Removed last fruit: mango (#fruits: 4)
-- fruits is now: {"banana", "grapes", "orange", "apple"}
local removed_at_pos = table.remove(fruits, 1) -- Removes first element ("banana")
print("Removed fruit at pos 1:", removed_at_pos, "(#fruits:", #fruits .. ")") -- Removed fruit at pos 1: banana (#fruits: 3)
-- fruits is now: {"grapes", "orange", "apple"}
local numbers = {5, 1, 100, 20, 3}
-- Default sort (ascending)
table.sort(numbers)
print("Sorted numbers (ascending):")
for i, v in ipairs(numbers) do print(string.format(" [%d] = %d", i, v)) end
-- Output: [1]=1, [2]=3, [3]=5, [4]=20, [5]=100
-- Custom sort (descending)
local names = {"Charlie", "Alice", "Bob"}
table.sort(names, function(a, b) return a > b end) -- Sort descending
print("\nSorted names (descending):")
for i, v in ipairs(names) do print(string.format(" [%d] = %s", i, v)) end
-- Output: [1]=Charlie, [2]=Bob, [3]=Alice

Lua provides two main iterators for tables:

  • ipairs(t): Iterates over integer keys 1, 2, ... (the sequence part) of table t until the first nil value. Ideal for arrays.
  • pairs(t): Iterates over all key-value pairs in table t in an unspecified order. Use this for dictionary-like tables or when you need to access all elements, including non-sequence parts.
local mixed_table = {
[1] = "first",
[2] = "second",
name = "My Table",
[4] = "fourth after nil", -- ipairs will stop before this
color = "blue"
}
mixed_table[3] = "third" -- now sequence is 1,2,3
print("\nIterating with ipairs (sequence part):")
for index, value in ipairs(mixed_table) do
print(string.format(" Index: %s, Value: %s", tostring(index), tostring(value)))
end
print("\nIterating with pairs (all elements):")
for key, value in pairs(mixed_table) do
print(string.format(" Key: %s, Value: %s", tostring(key), tostring(value)))
end

Further Resources: Tables are a vast topic. The official Lua Reference Manual is the definitive source for all details on table behavior, constructors, and the table library.