Tables
Work in progress
This page is not up to our standards yet.
This can be because is not complete or hasn't been reviewed and copyedited properly. Some details may be poorly worded, wrong, or unclear. Please let me (deviaze) know on Discord if you need clarification on anything.
A table stores values under keys. Any value except nil and NaN can be a key. A table with the keys 1, 2, 3 and so on is an array, and a table with other keys is a map. When those keys are names you know ahead of time, like a shop item's name and price, the table is a record.
Every table has an array portion and a hash portion. Consecutive integer keys starting at 1 are usually stored in the array portion and every other key goes in the hash portion. One table can use both.
# only measures the array portion. Using it on a map or a record is a mistake: it gives 0 for a table whose keys are all in the hash portion.
const gold = { Ada = 120, Grace = 75 }
print(#gold)0
Arrays
An array is a table of values in order. Write the values between braces separated by commas. Indices start at 1 rather than 0 and # gives the length:
const fruits = { "apple", "kiwi", "pear" }
print(fruits[1])
print(fruits[#fruits])
print(#fruits)apple pear 3
Adding and removing items
table.insert adds an item at the end or at an index you give it and table.remove takes one out and returns it. table.concat joins an array of strings or numbers into one string:
const queue = { "Ada", "Grace" }
table.insert(queue, "Alan") -- at the end
table.insert(queue, 1, "Linus") -- at index 1
const first = table.remove(queue, 1)
print(first)
print(table.concat(queue, ", "))Linus Ada, Grace, Alan
queue is a const and its contents still changed. const only stops the variable from pointing at a different table. The table itself can always change.
Sorting
table.sort sorts an array in place from smallest to largest. To sort by anything else, pass a function that says whether one item goes before another. Functions shows how.
const scores = { 72, 95, 88 }
table.sort(scores)
print(table.concat(scores, " "))72 88 95
Maps
A map (or dictionary) stores each value under a key. Almost any value can be a key: strings, numbers, booleans and even other tables. Write the key in square brackets:
const gold: { [string]: number? } = { ["Ada"] = 120, ["Grace"] = 75 }
gold["Alan"] = 30
print(gold["Alan"])
print(gold["Linus"])30 nil
Reading a key that isn't there gives nil instead of an error. Setting a key to nil removes it from the table.
Records
When the keys are names you know ahead of time, the table is a record. name = "Ada" is shorthand for ["name"] = "Ada" and player.name is shorthand for player["name"]. A record can hold other tables:
const player = {
name = "Ada",
level = 12,
position = { x = 4, y = 7 },
}
player.level += 1
print(`{player.name} is level {player.level}`)
print(`standing at {player.position.x}, {player.position.y}`)Ada is level 13 standing at 4, 7
Looping over tables
for key, value in t do visits every entry of t. An array's items come in order from index 1 and a map's entries come in no particular order.
const gold: { [string]: number? } = { Ada = 120, Grace = 75, Alan = 30 }
local total = 0
for _, amount in gold do
total += amount
end
print(total)225
Lua code loops over arrays with for i, v in ipairs(array) and over maps with for k, v in pairs(map). Both still run in Luwu but are deprecated, and the LuaIterators lint suggests the plain loop. ipairs stops at the first missing item, while pairs visits every entry in any order.
Holes in arrays
nil means a value isn't there. Assigning nil in the middle of an array leaves a hole that # may count up to or past. Keep arrays free of holes and use table.remove to take items out.
When an array needs a slot that's intentionally empty, Luwu's none primitive can be stored in a table where nil can't.
Tables are shared, not copied
Assigning a table to another variable doesn't copy it. Both variables refer to the same table and a change through one shows up through the other. table.clone makes a copy when you need one. The copy is shallow: tables inside it are still shared.
const original = { 1, 2, 3 }
const alias = original
alias[1] = 100
print(original[1])
const copy = table.clone(original)
copy[1] = 1
print(original[1])100 100
Table types
Luwu checks your code's types before it runs. Each kind of table has a different way to write its type:
-- an array of strings
const fruits: { string } = { "apple", "kiwi" }
-- a map from strings to numbers
const gold: { [string]: number? } = { Ada = 120 }
-- a record with named fields
type Player = { name: string, level: number }
const ada: Player = { name = "Ada", level = 12 }
print(fruits[1], gold.Ada, ada.name)The ? in number? means the value can also be nil. A map lookup can find nothing and the type says so. Type Checking covers types in more depth.
Going further
Table Comprehensions build a new table from a loop in one expression, and Destructuring pulls several fields out of a table at once.