Nested Tables and Structured Data
Build nested structures and access deep fields.
Nested Tables and Structured Data is a free Lua Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Lua Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Nested Table Basics
Tables can contain other tables as values, creating arbitrarily deep structures. Access nested fields by chaining dots or brackets. Missing intermediate tables cause errors — always ensure parent tables exist before accessing deep fields.
local company = {
name = "CoddyKit",
address = {
city = "Istanbul",
country = "Turkey",
zip = "34000"
},
employees = 25
}
print(company.name) -- CoddyKit
print(company.address.city) -- Istanbul
print(company["address"]["zip"]) -- 34000Safe Deep Access
Accessing a field on a nil value crashes with "attempt to index a nil value". Guard against this with the and short-circuit: t and t.a and t.a.b. Some codebases define a get(t, ...) helper that safely traverses a chain of keys.
local function get(t, ...)
local cur = t
for _, key in ipairs({...}) do
if type(cur) ~= "table" then return nil end
cur = cur[key]
end
return cur
end
local data = {user = {profile = {age = 30}}}
print(get(data, "user", "profile", "age")) -- 30
print(get(data, "user", "missing", "field")) -- nilArray of Records
A common pattern is an array of tables (records). Each element is a table with named fields. Iterate with ipairs and access fields by name. This is Lua's equivalent of an array of objects or a list of structs.
local users = {
{id=1, name="Alice", score=95},
{id=2, name="Bob", score=87},
{id=3, name="Carol", score=91},
}
for _, user in ipairs(users) do
print(user.id, user.name, user.score)
endBuilding Nested Tables Dynamically
Build nested structures incrementally by creating inner tables as needed. Always initialize a table before inserting into it. A common mistake is forgetting to create the inner table first, then trying to index nil.
local tree = {}
tree.root = {value = 10}
tree.root.left = {value = 5}
tree.root.right = {value = 15}
tree.root.left.left = {value = 2}
print(tree.root.value) -- 10
print(tree.root.left.value) -- 5
print(tree.root.left.left.value) -- 2Table as JSON-Like Config
Lua tables are often used for configuration files, similar to JSON or YAML. The file returns a table, and the application loads it with dofile() or require(). Nested tables represent nested config sections.
-- config.lua (returned table)
local config = {
server = {
host = "0.0.0.0",
port = 8080,
ssl = false,
},
db = {
host = "localhost",
name = "appdb",
pool_size = 10,
},
logging = { level = "info", file = "/var/log/app.log" }
}
print(config.server.port) -- 8080
print(config.db.pool_size) -- 10Graph as Adjacency List
Represent a graph as a table of tables: each key is a node, and its value is a list of neighbors. This adjacency list representation is memory-efficient for sparse graphs and easy to work with in Lua.
local graph = {
A = {"B", "C"},
B = {"A", "D"},
C = {"A", "D"},
D = {"B", "C"},
}
-- BFS from A
local visited = {A=true}
local queue = {"A"}
while #queue > 0 do
local node = table.remove(queue, 1)
io.write(node .. " ")
for _, neighbor in ipairs(graph[node]) do
if not visited[neighbor] then
visited[neighbor] = true
queue[#queue+1] = neighbor
end
end
end
print() -- A B C DModifying Nested Fields
Modify nested fields by accessing the parent table and assigning to the key. Since tables are passed by reference, any function that receives a table can modify its contents in-place — the caller sees the changes.
local player = {name="Hero", stats={hp=100, mp=50, atk=20}}
local function takeDamage(p, dmg)
p.stats.hp = math.max(0, p.stats.hp - dmg)
end
takeDamage(player, 35)
print(player.stats.hp) -- 65
player.stats.atk = player.stats.atk + 5
print(player.stats.atk) -- 25Serializing Nested Tables
Converting a nested table to a string is useful for debugging or logging. A simple recursive serializer handles any depth, though it won't handle cycles (circular references).
local function serialize(val, indent)
indent = indent or 0
local pad = string.rep(" ", indent)
if type(val) ~= "table" then
return tostring(val)
end
local parts = {"{"}
for k, v in pairs(val) do
parts[#parts+1] = pad.." "..tostring(k).."="..serialize(v,indent+1)
end
parts[#parts+1] = pad.."}"
return table.concat(parts, "\n")
end
local data = {x=1, nested={y=2, z=3}}
print(serialize(data))Sparse Matrices
A 2D matrix stored naively wastes memory for large sparse inputs. Store only non-zero entries in a nested table: matrix[row][col] = value. This can represent huge matrices efficiently when most values are zero.
local function newMatrix()
return setmetatable({}, {__index=function(t,k)
local row = {}
rawset(t, k, row)
return row
end})
end
local m = newMatrix()
m[1][1] = 5
m[3][7] = 12
m[100][200] = 99
print(m[1][1]) -- 5
print(m[3][7]) -- 12
print(m[2][5]) -- nil (sparse: no value)Deep Equality Check
Tables compare by reference with ==, not by value. To compare nested tables structurally, write a recursive equality function that compares each key-value pair and recurses into nested tables.
local function deepEqual(a, b)
if type(a) ~= type(b) then return false end
if type(a) ~= "table" then return a == b end
for k, v in pairs(a) do
if not deepEqual(v, b[k]) then return false end
end
for k in pairs(b) do
if a[k] == nil then return false end
end
return true
end
print(deepEqual({1,{2,3}},{1,{2,3}})) -- true
print(deepEqual({1,{2,3}},{1,{2,4}})) -- falseQuick Check
What happens when you access t.a.b and t.a is nil?
Recap: Nested Tables
Summary:
- Tables nest arbitrarily:
t.a.b.c - Guard deep access:
t and t.a and t.a.b - Tables are reference types — functions modify in-place
- Array of records:
{{name=...},{name=...}} - Sparse matrix: nested tables for memory efficiency
- Deep equality requires recursive comparison
Frequently asked questions
Is the “Nested Tables and Structured Data” lesson free?
Yes — the full text of “Nested Tables and Structured Data” is free to read here on the web, and the Lua Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Lua Academy course, upgrade to CoddyKit PRO.
What will I learn in “Nested Tables and Structured Data”?
Build nested structures and access deep fields. You practise Lua Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Lua Academy?
No prior experience is required. Lua Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Nested Tables and Structured Data” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Lua Academy lesson?
Yes. Every Lua Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Tables as Arrays
- Tables as Dictionaries
- Iterating Tables with pairs and ipairs
- Nested Tables and Structured Data