Lua - Tables
Overview
Estimated time: 30–40 minutes
Tables are Lua's only data structure, but they're incredibly versatile. They can serve as arrays, dictionaries, objects, sets, and more. This tutorial covers all aspects of table usage in Lua.
Learning Objectives
- Understand table creation and initialization
- Master array-style and dictionary-style table usage
- Learn table traversal with pairs and ipairs
- Explore advanced table operations and the table library
- Apply tables for object-oriented programming patterns
Prerequisites
- Understanding of Lua variables and data types
- Basic knowledge of functions
Table Creation
Empty Tables
-- Creating empty tables
local empty_table = {}
local another_empty = {}
print("Type of empty_table:", type(empty_table))
print("Length of empty table:", #empty_table)
-- Tables are reference types
local ref1 = empty_table
local ref2 = empty_table
print("ref1 == ref2:", ref1 == ref2) -- true, same reference
print("empty_table == {}:", empty_table == {}) -- false, different tables
Expected Output:
Type of empty_table: table
Length of empty table: 0
ref1 == ref2: true
empty_table == {}: false
Table Literals
-- Array-style initialization
local fruits = {"apple", "banana", "orange", "grape"}
local numbers = {10, 20, 30, 40, 50}
local mixed = {1, "hello", true, 3.14}
print("First fruit:", fruits[1]) -- Lua arrays are 1-indexed!
print("Second number:", numbers[2])
print("Mixed table:", mixed[1], mixed[2], mixed[3], mixed[4])
-- Dictionary-style initialization
local person = {
name = "Alice",
age = 30,
city = "New York",
is_student = false
}
print("Person name:", person.name)
print("Person age:", person["age"]) -- Alternative syntax
Expected Output:
First fruit: apple
Second number: 20
Mixed table: 1 hello true 3.14
Person name: Alice
Person age: 30
Array-Style Tables
Creating and Accessing Arrays
local colors = {"red", "green", "blue", "yellow"}
-- Accessing elements (1-indexed)
print("First color:", colors[1])
print("Last color:", colors[#colors]) -- # gives length
-- Adding elements
colors[5] = "purple"
colors[#colors + 1] = "orange" -- Append to end
print("Array length:", #colors)
for i = 1, #colors do
print("Color " .. i .. ":", colors[i])
end
Expected Output:
First color: red
Last color: yellow
Array length: 6
Color 1: red
Color 2: green
Color 3: blue
Color 4: yellow
Color 5: purple
Color 6: orange
Multi-dimensional Arrays
-- 2D array (matrix)
local matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
-- Accessing 2D array elements
print("Element at [2][3]:", matrix[2][3]) -- 6
-- Creating a 3x3 matrix dynamically
local grid = {}
for i = 1, 3 do
grid[i] = {}
for j = 1, 3 do
grid[i][j] = i * 10 + j
end
end
-- Print the grid
for i = 1, 3 do
local row = ""
for j = 1, 3 do
row = row .. grid[i][j] .. " "
end
print("Row " .. i .. ":", row)
end
Expected Output:
Element at [2][3]: 6
Row 1: 11 12 13
Row 2: 21 22 23
Row 3: 31 32 33
Dictionary-Style Tables
Key-Value Pairs
local student = {
name = "John Doe",
age = 20,
grade = "A",
subjects = {"Math", "Physics", "Chemistry"}
}
-- Different ways to access values
print("Name (dot notation):", student.name)
print("Age (bracket notation):", student["age"])
-- Adding new key-value pairs
student.gpa = 3.8
student["graduation_year"] = 2024
-- Keys can be variables
local key = "grade"
print("Grade using variable key:", student[key])
-- Keys can be any type (except nil)
local complex_table = {
[1] = "numeric key",
["string_key"] = "string value",
[true] = "boolean key",
[{nested = "table"}] = "table key" -- Not recommended
}
print("Numeric key:", complex_table[1])
print("String key:", complex_table.string_key)
print("Boolean key:", complex_table[true])
Expected Output:
Name (dot notation): John Doe
Age (bracket notation): 20
Grade using variable key: A
Numeric key: numeric key
String key: string value
Boolean key: boolean key
Table Traversal
Using pairs() for All Elements
local inventory = {
sword = 1,
shield = 1,
potion = 5,
gold = 100,
[1] = "first item",
[2] = "second item"
}
print("=== Using pairs() ===")
for key, value in pairs(inventory) do
print(key .. ":", value)
end
Expected Output:
=== Using pairs() ===
1: first item
2: second item
sword: 1
shield: 1
potion: 5
gold: 100
Using ipairs() for Arrays
local animals = {"cat", "dog", "bird", "fish"}
print("=== Using ipairs() ===")
for index, animal in ipairs(animals) do
print("Animal " .. index .. ":", animal)
end
-- ipairs stops at the first nil
local sparse_array = {"a", "b", nil, "d"}
print("\n=== Sparse array with ipairs() ===")
for i, v in ipairs(sparse_array) do
print(i, v) -- Only prints 1: a, 2: b
end
print("\n=== Sparse array with numeric for loop ===")
for i = 1, 4 do
print(i, sparse_array[i]) -- Shows all including nil
end
Expected Output:
=== Using ipairs() ===
Animal 1: cat
Animal 2: dog
Animal 3: bird
Animal 4: fish
=== Sparse array with ipairs() ===
1 a
2 b
=== Sparse array with numeric for loop ===
1 a
2 b
3 nil
4 d
Table Operations
Table Library Functions
local fruits = {"apple", "banana"}
-- table.insert - add elements
table.insert(fruits, "orange") -- Append to end
table.insert(fruits, 2, "grape") -- Insert at position 2
print("After insertions:")
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
-- table.remove - remove elements
local removed = table.remove(fruits, 2) -- Remove at position 2
print("Removed fruit:", removed)
local last = table.remove(fruits) -- Remove last element
print("Last fruit:", last)
print("After removals:")
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
-- table.concat - join array elements
local numbers = {1, 2, 3, 4, 5}
print("Joined numbers:", table.concat(numbers, "-"))
print("Partial join:", table.concat(numbers, ", ", 2, 4)) -- Elements 2-4
Expected Output:
After insertions:
1 apple
2 grape
3 banana
4 orange
Removed fruit: grape
Last fruit: orange
After removals:
1 apple
2 banana
Joined numbers: 1-2-3-4-5
Partial join: 2, 3, 4
Table Sorting
-- Sorting numeric arrays
local numbers = {5, 2, 8, 1, 9, 3}
table.sort(numbers)
print("Sorted numbers:", table.concat(numbers, ", "))
-- Sorting strings
local names = {"Charlie", "Alice", "Bob", "David"}
table.sort(names)
print("Sorted names:", table.concat(names, ", "))
-- Custom sort function
local students = {
{name = "Alice", grade = 85},
{name = "Bob", grade = 92},
{name = "Charlie", grade = 78}
}
-- Sort by grade (descending)
table.sort(students, function(a, b)
return a.grade > b.grade
end)
print("Students sorted by grade:")
for i, student in ipairs(students) do
print(student.name .. ":", student.grade)
end
Expected Output:
Sorted numbers: 1, 2, 3, 5, 8, 9
Sorted names: Alice, Bob, Charlie, David
Students sorted by grade:
Bob: 92
Alice: 85
Charlie: 78
Tables as Objects
Simple Object Pattern
-- Creating objects with tables
local function create_person(name, age)
local person = {
name = name,
age = age,
-- Methods
greet = function(self)
return "Hello, I'm " .. self.name
end,
get_age = function(self)
return self.age
end,
have_birthday = function(self)
self.age = self.age + 1
print(self.name .. " is now " .. self.age .. " years old")
end
}
return person
end
-- Using the object
local alice = create_person("Alice", 25)
print(alice:greet()) -- Using colon notation
print("Age:", alice:get_age())
alice:have_birthday()
local bob = create_person("Bob", 30)
print(bob:greet())
Expected Output:
Hello, I'm Alice
Age: 25
Alice is now 26 years old
Hello, I'm Bob
Class-like Pattern
-- Class-like pattern with shared methods
local Rectangle = {}
Rectangle.__index = Rectangle
function Rectangle.new(width, height)
local rect = {
width = width,
height = height
}
setmetatable(rect, Rectangle)
return rect
end
function Rectangle:area()
return self.width * self.height
end
function Rectangle:perimeter()
return 2 * (self.width + self.height)
end
function Rectangle:scale(factor)
self.width = self.width * factor
self.height = self.height * factor
end
-- Using the Rectangle class
local rect1 = Rectangle.new(5, 3)
print("Rectangle area:", rect1:area())
print("Rectangle perimeter:", rect1:perimeter())
rect1:scale(2)
print("After scaling - area:", rect1:area())
Expected Output:
Rectangle area: 15
Rectangle perimeter: 16
After scaling - area: 60
Advanced Table Techniques
Table Copying
-- Shallow copy
local function shallow_copy(t)
local copy = {}
for k, v in pairs(t) do
copy[k] = v
end
return copy
end
-- Deep copy (recursive)
local function deep_copy(t)
if type(t) ~= "table" then
return t
end
local copy = {}
for k, v in pairs(t) do
copy[deep_copy(k)] = deep_copy(v)
end
return copy
end
local original = {
name = "Alice",
scores = {math = 95, english = 87}
}
local shallow = shallow_copy(original)
local deep = deep_copy(original)
-- Modify nested table
original.scores.math = 100
print("Original math score:", original.scores.math)
print("Shallow copy math score:", shallow.scores.math) -- Also changed!
print("Deep copy math score:", deep.scores.math) -- Unchanged
Expected Output:
Original math score: 100
Shallow copy math score: 100
Deep copy math score: 95
Table as Set
-- Using tables as sets
local function create_set(list)
local set = {}
for _, item in ipairs(list) do
set[item] = true
end
return set
end
local function has_item(set, item)
return set[item] == true
end
local function set_union(set1, set2)
local result = {}
for k in pairs(set1) do result[k] = true end
for k in pairs(set2) do result[k] = true end
return result
end
local fruits_set = create_set({"apple", "banana", "orange"})
local colors_set = create_set({"red", "green", "blue", "orange"})
print("Has apple:", has_item(fruits_set, "apple"))
print("Has red:", has_item(fruits_set, "red"))
local combined = set_union(fruits_set, colors_set)
print("Combined set items:")
for item in pairs(combined) do
print("-", item)
end
Expected Output:
Has apple: true
Has red: false
Combined set items:
- orange
- apple
- banana
- red
- green
- blue
Common Pitfalls
- 1-indexed arrays: Lua arrays start at index 1, not 0
- Length operator:
#
only works correctly with array-like tables - Reference vs value: Tables are passed by reference, not copied
- nil values: Setting a table element to nil removes it from the table
- Sparse arrays: Arrays with nil values can cause unexpected behavior
Checks for Understanding
- What index does the first element of a Lua array have?
- What's the difference between pairs() and ipairs()?
- How do you get the length of an array-style table?
- What happens when you assign nil to a table element?
- How do you add an element to the end of an array?
Show answers
- Index 1 - Lua arrays are 1-indexed, not 0-indexed
- pairs() iterates over all key-value pairs; ipairs() only iterates over consecutive integer keys starting from 1
- Use the length operator
#table_name
- The element is removed from the table completely
- Use
table.insert(array, value)
orarray[#array + 1] = value
Next Steps
Tables are fundamental to Lua programming. Now that you understand how they work, you're ready to explore arrays in more detail and learn about iterators for advanced table processing.