Skip to content

Lua Tables Guide — Arrays, Dictionaries, and Mixed Data Structures

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Lua Tables Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Lua tables are the single data structure that serves as arrays, dictionaries, objects, modules, and sets -- combining associative array semantics with array-like indexed access into one flexible container.

What You'll Learn

  • Creating and accessing tables as arrays and dictionaries
  • 1-based indexing and the length operator
  • Mixed tables with both integer and string keys
  • Table manipulation functions in the standard library

Why It Matters

Tables are the foundation of every Lua program. Understanding tables means understanding Lua -- OOP, metatables, modules, and even environments all use tables. Mastering tables makes every other Lua concept easier to learn.

Real-World Use

A Redis Lua script uses a table to store multiple keys and values returned from a hash operation. A Love2D game uses tables to store all game objects, their positions, and properties. A configuration parser stores its entire parsed result in a nested table.

flowchart LR
    A["Tables"] --> B["Array Style"]
    A --> C["Dict Style"]
    A --> D["Mixed Style"]
    B --> E["Operations"]
    C --> E
    D --> E
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Creating Tables

Tables are created with curly braces {}.

-- Empty table
local t = {}

-- Array-style table (implicit integer keys)
local fruits = {"apple", "banana", "cherry"}

-- Dictionary-style table (explicit keys)
local person = {
    name = "Alice",
    age = 30,
    job = "Engineer"
}

-- Mixed table
local mixed = {
    "first",
    "second",
    key = "value",
    [10] = "ten",
    ["nested"] = {1, 2, 3}
}

Array-Style Access

Arrays are 1-indexed in Lua. The first element is at index 1.

local colors = {"red", "green", "blue"}

-- Access by index (1-based)
print(colors[1])  --> red
print(colors[2])  --> green
print(colors[3])  --> blue

-- Length operator
print(#colors)    --> 3

-- Adding elements
colors[4] = "yellow"
colors[#colors + 1] = "purple"
print(#colors)    --> 5

-- Iterating
for i = 1, #colors do
    print(i, colors[i])
end

Expected output:

red
green
blue
3
5
1    red
2    green
3    blue
4    yellow
5    purple

Important: 1-Indexed

Lua arrays start at 1, not 0. This is a common source of bugs for developers coming from most other languages.

local arr = {10, 20, 30}
print(arr[0])  --> nil (no error, just nil!)
print(arr[1])  --> 10

Dictionary-Style Access

Tables can use strings, numbers, or any value as keys.

local config = {
    host = "localhost",
    port = 8080,
    debug = true
}

-- Dot notation (for string keys)
print(config.host)    --> localhost
print(config.port)    --> 8080

-- Bracket notation (for any key type)
print(config["host"]) --> localhost
print(config["port"]) --> 8080

-- Adding new keys
config.timeout = 5000
config["retries"] = 3

-- Checking existence
if config.debug then
    print("Debug mode is on")
end

Mixed Tables

Tables can mix array and dictionary parts freely.

local data = {
    "alpha",       -- [1] = "alpha"
    "beta",        -- [2] = "beta"
    name = "test",  -- ["name"] = "test"
    [5] = "five",  -- explicit key
    "gamma"        -- [3] = "gamma" (continues from 3)
}

print(data[1])    --> alpha
print(data[2])    --> beta
print(data[3])    --> gamma
print(data.name)  --> test
print(data[5])    --> five

Length of Mixed Tables

The # operator only counts the array part (contiguous integer keys from 1).

local t = {10, 20, nil, 40}
print(#t)  --> ? (undefined behavior -- table has a hole at 3)

local t2 = {a = 1, b = 2}
print(#t2) --> 0 (dictionary part not counted)

Nested Tables

Tables can contain other tables, creating complex data structures.

local matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
}
print(matrix[2][3])  --> 6

local inventory = {
    player = {
        name = "Hero",
        items = {"sword", "shield", "potion"}
    },
    chest = {
        gold = 100,
        items = {"key", "map"}
    }
}
print(inventory.player.items[1])  --> sword

Table Library Functions

local t = {3, 1, 4, 1, 5, 9}

-- Insert at end
table.insert(t, 2)
--> {3, 1, 4, 1, 5, 9, 2}

-- Insert at position
table.insert(t, 1, 100)
--> {100, 3, 1, 4, 1, 5, 9, 2}

-- Remove and return last element
local last = table.remove(t)
print(last)  --> 2

-- Remove from position
local first = table.remove(t, 1)
print(first) --> 100

-- Sort (in-place)
table.sort(t)
--> {1, 1, 3, 4, 5, 9}

-- Concatenate
local words = {"Lua", "is", "great"}
print(table.concat(words, " "))  --> Lua is great
print(table.concat(words, ", ", 1, 2))  --> Lua, is

Iterating Over Tables

local t = {a = 1, b = 2, c = 3}

-- pairs() iterates all key-value pairs
for key, value in pairs(t) do
    print(key, value)
end

-- ipairs() iterates integer keys from 1
local arr = {10, 20, 30}
for index, value in ipairs(arr) do
    print(index, value)
end

Common Mistakes

1. Using index 0 instead of 1

fruits[0] returns nil. Always start loops at 1. This is the most common Lua bug.

2. Assuming tables copy by value

local t2 = t1 creates an alias, not a copy. Use table.clone (Lua 5.4+) or a loop for deep copies.

3. Modifying tables while iterating

Adding or removing keys during pairs() iteration produces undefined behavior. Collect keys first, then modify.

4. Expecting # to work on dictionaries

The # operator only counts the array part (contiguous 1-based integer keys). It returns 0 for pure dictionaries.

5. Assuming insertion order is preserved

pairs() does NOT guarantee iteration order (though Lua 5.4 preserves it for performance). Use ipairs for ordered arrays.

Practice Questions

1. What is the value of arr[0] in Lua when arr = {10, 20, 30}? nil. Lua arrays start at 1, so index 0 is not a valid entry.

2. How do you add an element to the end of an array? Use table.insert(arr, value) or arr[#arr + 1] = value.

3. What is the difference between pairs() and ipairs()? ipairs() iterates integer keys from 1 sequentially. pairs() iterates all key-value pairs. Use ipairs for arrays and pairs for dictionaries.

Challenge: Create a table representing a student database with at least 3 students. Each student has name, age, and grades (a table of subjects and scores). Print each student's name and average grade.

FAQ

{{< faq question="Why does Lua use 1-based indexing?" >}} Lua was designed for non-programmers (domain experts, artists) who naturally count from 1. The designers prioritized approachability over convention. {{< /faq >}}

{{< faq question="Can tables have non-string or non-integer keys?" >}} Yes. Tables can use any value as a key except nil. This includes booleans, functions, and other tables as keys. {{< /faq >}}

{{< faq question="How do I copy a table?" >}} For shallow copy in Lua 5.4+: local copy = table.clone(original). For deep copy, use a recursive function that creates new tables for nested tables. {{< /faq >}}

{{< faq question="Is a table an array or a hash map?" >}} Both. Lua tables use a hybrid implementation -- the array part stores contiguous integer keys from 1, and the hash part stores everything else. This makes them efficient as both arrays and dictionaries. {{< /faq >}}

{{< faq question="How do I check if a table is empty?" >}} Use next(t) == nil. If next(t) returns nil, the table has no elements. Using #t == 0 only checks the array part. {{< /faq >}}

Try It Yourself

local shopping_list = {"milk", "bread", "eggs", "cheese"}

-- Add items
shopping_list[#shopping_list + 1] = "butter"

-- Display all items
print("Shopping List (" .. #shopping_list .. " items):")
for i, item in ipairs(shopping_list) do
    print(i .. ". " .. item)
end

-- Find an item
local target = "eggs"
for i, item in ipairs(shopping_list) do
    if item == target then
        print(target .. " is at position " .. i)
        break
    end
end

Expected output:

Shopping List (5 items):
1. milk
2. bread
3. eggs
4. cheese
5. butter
eggs is at position 3

What's Next

Now that you understand tables, learn how functions work in Lua -- including first-class functions, closures, and varargs.

Topic Description Link
Functions First-class functions and closures {{< ref "05-functions" >}}
Metatables Operator overloading and inheritance {{< ref "06-metatables" >}}
Python Lists Compare with Python lists and dicts Python

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro