0Pricing
Lua Academy · Lesson

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"]) -- 34000

Safe 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"))  -- nil

Array 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)
end

Building 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)  -- 2

Table 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)    -- 10

Graph 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 D

Modifying 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)  -- 25

Serializing 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}}))  -- false

Quick 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

  1. Tables as Arrays
  2. Tables as Dictionaries
  3. Iterating Tables with pairs and ipairs
  4. Nested Tables and Structured Data
← Back to Lua Academy