Home Courses Lua Day 2
Day 02 Day 2

Day 2

Day 2

~1 hour Intermediate Hands-on Precision AI Academy

Today's Objective

Arrays, dictionaries, namespaces, and modules — all built on a single data structure. Master tables and you understand 80% of Lua.

What Is a Table?

A table in Lua is an associative array: it maps keys to values. Keys can be any Lua value except nil and NaN. This one data structure replaces arrays, dictionaries, sets, objects, namespaces, and modules in every other language.

Topics today: table construction, array-style tables, mixed tables, ipairs vs pairs, table.* library functions, nested tables, and the module pattern.

Creating and Accessing Tables

lua_-_table_basics.txt
LUA — TABLE BASICS
-- Empty table
local t = {}

-- Dictionary-style: string keys
local person = {
  name  = "Alice",
  age   = 30,
  email = "[email protected]",
}

-- Access with dot notation or bracket notation
print(person.name)           --> Alice
print(person["age"])         --> 30

-- Add or update keys
person.city = "Denver"
person["age"] = 31

-- Remove a key (set to nil)
person.email = nil

-- Any value (except nil/NaN) can be a key
local t2 = {}
t2[1]    = "one"
t2[true] = "yes"
t2[3.14] = "pi"

-- Tables are passed by reference
local a = {x = 1}
local b = a       -- b points to the same table
b.x = 99
print(a.x)        --> 99 (not a copy!)

Array-Style Tables

When you use integer keys starting at 1, Lua treats the table like an array. The # length operator returns the last integer key in the sequence (it assumes no gaps).

lua_-_arrays.txt
LUA — ARRAYS
-- Array constructor
local fruits = {"apple", "banana", "cherry", "date"}

print(#fruits)        --> 4
print(fruits[1])      --> apple  (1-indexed!)
print(fruits[#fruits])  --> date   (last element)

-- table.insert: append or insert at position
table.insert(fruits, "elderberry")        -- append
table.insert(fruits, 2, "avocado")        -- insert at index 2

-- table.remove: remove at position (default: last)
local removed = table.remove(fruits, 2)   -- removes "avocado"
print(removed)   --> avocado

-- table.concat: join to string
print(table.concat(fruits, ", "))
--> apple, banana, cherry, date, elderberry

-- table.sort: in-place sort
table.sort(fruits)
print(table.concat(fruits, ", "))
--> apple, banana, cherry, date, elderberry (alphabetical)

-- Custom sort comparator
table.sort(fruits, function(a, b) return #a < #b end)
print(table.concat(fruits, ", "))
--> date, apple, banana, cherry, elderberry (by length)

-- WARNING: #t is unreliable if the array has gaps (holes)
local sparse = {1, 2, nil, 4}
print(#sparse)  -- undefined behavior: could be 2 or 4

ipairs vs pairs

ipairs iterates over integer keys 1, 2, 3... stopping at the first nil. pairs iterates over all key-value pairs in arbitrary order. Use ipairs for arrays, pairs for dictionaries.

lua_-_iterators.txt
LUA — ITERATORS
local mixed = {10, 20, 30, name="Alice", city="Denver"}

-- ipairs: only integer keys 1, 2, 3 ...
print("ipairs:")
for i, v in ipairs(mixed) do
  print(i, v)
end
-- 1   10
-- 2   20
-- 3   30
-- (does NOT print name or city)

-- pairs: ALL key-value pairs, any order
print("pairs:")
for k, v in pairs(mixed) do
  print(k, v)
end
-- 1     10
-- 2     20
-- 3     30
-- name  Alice
-- city  Denver

-- next(): the raw iterator used by pairs
-- useful for checking if a table is empty
local function is_empty(t)
  return next(t) == nil
end
print(is_empty({}))        --> true
print(is_empty({1,2,3}))   --> false

Nested Tables and Deep Copies

lua_-_nested_tables.txt
LUA — NESTED TABLES
-- Nested tables
local config = {
  server = {
    host = "localhost",
    port = 8080,
    tls  = false,
  },
  database = {
    url     = "postgres://localhost/mydb",
    pool    = 10,
    timeout = 30,
  },
}

print(config.server.port)           --> 8080
print(config.database.pool)         --> 10

-- 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(orig)
  local copy
  if type(orig) == "table" then
    copy = {}
    for k, v in pairs(orig) do
      copy[deep_copy(k)] = deep_copy(v)
    end
    setmetatable(copy, getmetatable(orig))
  else
    copy = orig
  end
  return copy
end

local orig = {a = {1, 2, 3}, b = "hello"}
local c    = deep_copy(orig)
c.a[1] = 99
print(orig.a[1])  --> 1 (not affected)

The Module Pattern

Lua doesn't have a built-in module system but tables make it trivial to build one. The standard pattern is to return a table from a file and require it elsewhere.

lua_-_module_pattern.txt
LUA — MODULE PATTERN
-- math_utils.lua  (this is the module file)
local M = {}  -- 'M' is conventional for the module table

local function clamp(v, lo, hi)  -- private: not exported
  return math.min(math.max(v, lo), hi)
end

function M.lerp(a, b, t)
  t = clamp(t, 0, 1)
  return a + (b - a) * t
end

function M.sign(x)
  if x > 0 then return 1
  elseif x < 0 then return -1
  else return 0
  end
end

function M.round(x, decimals)
  local factor = 10 ^ (decimals or 0)
  return math.floor(x * factor + 0.5) / factor
end

return M  -- MUST return the module table

-- ─────────────────────────────────────────
-- main.lua (consumer)
local mu = require("math_utils")

print(mu.lerp(0, 100, 0.25))   --> 25.0
print(mu.sign(-7))              --> -1
print(mu.round(3.14159, 2))     --> 3.14
require caches modules. Calling require("math_utils") multiple times returns the same table — Lua only runs the file once and stores the result in package.loaded. This is safe and efficient.
Exercise
Build a Stack and a Queue Module
  1. Create stack.lua with a new() function that returns a stack object. The stack should have push(v), pop(), peek(), size(), and is_empty() methods — all backed by a table.
  2. Create queue.lua similarly with enqueue(v), dequeue(), front(), size(), and is_empty(). Use two indices (head and tail) to avoid O(n) removal.
  3. Write a test script that creates both structures, pushes 10 items, pops them all, and verifies the order (LIFO for stack, FIFO for queue).
  4. Add a to_array() method to both that returns all current elements as a plain Lua array.
  5. Modify stack.new() to accept an optional initial array and pre-populate the stack from it.

Implement a Set module using tables. Support new(list), add(v), remove(v), contains(v), union(s1, s2), intersection(s1, s2), and difference(s1, s2). Use boolean table values (t[v] = true) for O(1) membership testing.

What's Next

The foundations from today carry directly into Day 3. In the next session the focus shifts to Day 3 — building directly on everything covered here.

Supporting Videos & Reading

Go deeper with these external references.

Day 2 Checkpoint

Before moving on, verify you can answer these without looking:

Live Bootcamp

Learn this in person — 2 days, 5 cities

Thu–Fri sessions in Denver, Los Angeles, New York, Chicago, and Dallas. $1,490 per seat. June–October 2026.

Reserve Your Seat →
Continue To Day 3
Day 3: Metatables, OOP, and __index