23. Tables as dictionaries

The table type used for lists also supports named keys. With names (strings) instead of positions (numbers), the table acts like a dictionary — a collection of facts about one thing.

Creating a dictionary

The literal syntax uses key = value inside the braces:

local player = {
    name = "Keiko",
    level = 7,
    hp = 95,
    alive = true,
}

The trailing comma is optional, but good style: it makes adding a field a one-line diff later.

Read values with dot notation or bracket notation:

print(player.name)        -- Keiko
print(player["level"])    -- 7

player.name and player["name"] are identical. Use dot notation for a plain word; use brackets when the key is in a variable or has spaces:

local field = "hp"
print(player[field])   -- 95   -- dot would not work here

Adding and changing fields

Assigning to a missing key creates it; assigning to an existing key replaces it; assigning nil deletes it:

player.mana = 30         -- new field
player.hp = 100          -- replace
player.alive = nil       -- delete

After those three lines, player holds name, level, hp, mana.

Looping with pairs

pairs is the dictionary iterator. It yields every key and value, in unspecified order:

for key, value in pairs(player) do
    print(key, value)
end

Every field prints, but not necessarily in the order you wrote. If you need a specific order, store the keys in a list and loop that — or use ipairs with a list shape instead.

On a list, pairs does include the numeric keys, but in no guaranteed order. For lists, always use ipairs; for dictionaries, use pairs.

Checking if a key exists

A missing key looks up as nil, which gives you an easy check:

if player.email then
    print(player.email)
else
    print("No email on file.")
end

This works because nil is falsy and any non-nil value is truthy.

Nested tables

A table value can be another table — the standard way to build structured data:

local player = {
    name = "Keiko",
    position = { x = 0, y = 5, z = -3 },
    inventory = {"sword", "potion", "map"},
}

print(player.position.x)          -- 0
print(player.inventory[1])        -- sword

In Roblox, the in-game world is one big nested table of this shape. game.Players.Keiko.Character is just a chain of dot lookups walking the tree, so this chapter's skills apply directly.

Lists and dictionaries together

One table can hold both numeric and named keys at once:

local quest = {
    "Speak to the elder",
    "Collect 5 herbs",
    "Return to camp",
    name = "Healer's Errand",
    reward = 50,
}

print(quest.name)        -- Healer's Errand
print(quest[1])          -- Speak to the elder
print(#quest)            -- 3   (only the list part counts)

# and ipairs only see the list part; pairs walks everything. Tangled-sounding but handy: one table can hold both an ordered list of items and some side facts about it.

Homework

Problem 1 — Player record

Open exercises/23/homework/01-player-record.lua. Build a player table with at least these four keys: name (string), class (string), level (number), alive (boolean). Then loop with for ... in pairs(...) to print every key and value, one per line.

Problem 2 — Inventory weights

Open exercises/23/homework/02-inventory-weights.lua. A table maps item names to weights (numbers). Compute and print the total weight.

Problem 3 — Safe lookup

Open exercises/23/homework/03-safe-lookup.lua. Write local function lookup(dict, key) that returns the value if the key exists, else the string "(not found)". Test it with three calls: one that hits, one that misses, and one where the key is held in a variable.

Challenge — Word frequency

Open exercises/23/homework/04-word-frequency.lua. Given a list of words (some repeated), build a dictionary mapping each unique word to its count. Print the dictionary one pair per line.

Stuck or finished? Open the homework solutions page.