Skip to content
Lesson 9 of 18

Step 1 of 5 · Reading · ~3 min

Learn

Tables — The One Data Structure

Tables as Arrays

Lua has exactly one container type: the table. No separate array, list, dictionary, set, queue or object — a table is an associative map from keys to values, and when its keys happen to be the integers 1, 2, 3 and so on, it behaves as an array. Every other data structure you will ever build in Lua is a table wearing a costume.

One is the first index

Lua counts from 1, and the whole standard library agrees: #, ipairs, table.insert, table.remove, table.concat and table.sort all assume the first element lives at index 1.

local fruits = {"apple", "banana", "cherry"}
print(fruits[1])
print(fruits[3])
print(#fruits)

--> apple
--> cherry
--> 3
fruits = {"apple", "banana", "cherry"}

  key       1          2          3
  value  "apple"   "banana"   "cherry"
            ^
            first element — there is no fruits[0]

Reading fruits[0] is not an error. It returns nil, quietly, exactly like any other absent key. That is why a 0-based habit shows up as an arithmetic-on-nil error two lines later instead of as an out-of-bounds message.

Growing and shrinking

local q = {"a", "b", "c"}
table.insert(q, "d")
table.insert(q, 1, "z")
print(table.concat(q, ","))
table.remove(q, 1)
print(table.concat(q, ","))
print(#q)

--> z,a,b,c,d
--> a,b,c,d
--> 4

table.insert(t, v) appends. table.insert(t, pos, v) inserts at pos and shifts everything above it up. table.remove(t, pos) deletes at pos, shifts everything down to close the gap, and returns the value it removed. Writing t[#t + 1] = v appends just as well and is a common sight in real code.

Walking the array

ipairs yields index and value starting at 1, and stops at the first missing index.

local scores = {10, 20, 30}
for i, v in ipairs(scores) do
    print(i .. ":" .. v)
end

--> 1:10
--> 2:20
--> 3:30

When you only care about the values, the convention is to name the index _.

local total = 0
for _, v in ipairs(scores) do
    total = total + v
end
print(total)

--> 60

What a hole does to a table

Assigning nil does not blank a slot — it removes the key entirely. The table now has a hole, and both # and ipairs stop promising what you want.

local t = {10, 20, 30}
t[2] = nil
for i, v in ipairs(t) do
    print(i .. ":" .. v)
end

--> 1:10

t[3] still exists and still holds 30; ipairs simply refuses to step across the gap. #t is murkier still: the manual defines it as a border of the table, and a table with a hole has more than one valid border, so #t may legitimately answer 1 or 3 here. Do not build anything on that answer.

✓ keep sequences dense — delete with table.remove, which closes the gap ✗ t[i] = nil in the middle of a list you still intend to iterate or measure

Tables are references

A table variable holds a reference, not a copy, so two names can point at the same table.

local a = {1, 2}
local b = a
b[1] = 99
print(a[1])

--> 99

Passing a table to a function passes that same reference, which means the function can rewrite your table from the inside. Usually that is exactly what you want; always it is something to remember.

Your exercise

Sum of List reads a count N, then N integers on their own lines, and prints the total.

The starter already reads everything into a table called nums, so you add the accumulation and one print.

The mistake the grader will catch is the 0-based habit: for i = 0, n - 1 do total = total + nums[i] end reads nums[0], gets nil, and the run dies with attempt to perform arithmetic on a nil value. Write for i = 1, n or, better, for _, v in ipairs(nums). Watch one more: declaring local total = 0 inside the loop resets it every pass so you print only the last number — and the hidden test feeds a single value, so that version still passes it and hides the bug from you.

Up nextTables as DictionariesTables — The One Data Structure

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…