Tutorial; Tables - HWRM/KarosGraveyard GitHub Wiki

Tables

Tables are Lua's only data structure, and are just associate arrays (key-value pairs).

❌ Many Lua resources will try to get you using the functions pairs and ipairs, as well as the # syntax for checking a table's length. None of these are defined in Lua 4.x, much less in HWRM's flavour of the language. Use a basic for loop for iteration. Always keep in mind that most documentation about Lua is written for Lua 5!

Basics

We can use tables primarily to store key-value pairs:

-- the syntax for key-value pairs in a table is
-- 1.: 'key = value,', i.e 'x = 10,'
-- 2.: [<stringifyable>] = value

t = {
  x = 10, -- 'x' is a string
  y = 20,
  z = "hi",
  ['hello!'] = 100,
  [9 + 10] = "twenty one", -- the number result of 9+10 can be cast to a string automatically
  t = {} -- you can omit the final comma if you wish
};

t['x']; -- 10
t['z']; -- 'hi'
t['a']; -- nil
t[19]; -- 'twenty one'
t['t']; -- 'Table: <memory address>'

You can access a table's keys with 'dot notation' too:

t = {
  foo = "bar",
  ["hello"] = 20
};
t[20] = "???";

t.foo; -- "bar"
t.hello; -- 20
t.20; -- error: illegal syntax

💡 Note that you may only use dot notation with keys which could otherwise be a valid 'identifier', which just means you could otherwise have used it as a variable name.

Arrays

Table values may also be unkeyed, which causes Lua to automatically assign the value to an index instead:

t = {
  "hello", -- no key given, will be assigned to key 1
  45,
  50
};
t['x'] = 10; -- can't mix keyed and unkeyed in the same assignment (no discernable reason)

t['x']; -- 10
t[0]; -- nil
t[1]; -- 'hello'
t[2]; -- 45
t[3]; -- 50

Tables with only indexes and no keys are referred to sometimes as 'arrays', although this is a rather misleading name.

💡 Keep in mind that Lua tables begin their indexing at 1, rather than 0 as seen is almost any other language.

Tables as arrays are generally used with the three functions getn, tinsert, and tremove:

arr = {};

tinsert(arr, 10);
tinsert(arr, "world");

getn(arr); -- 2 ({ [1] = 10, [2] = "world", n = 2 })

-- call with 3 args:
tinsert(arr, 2, "hello"); -- ({ [1] = 10, [2] = "hello", [3] = "world", n = 3 })

tremove(arr, 2); -- ({ [1] = 10, [2] = "world", n = 2 })

Note that tinsert and tremove both add a key to the table, "n", which is used to track the length of the array (aka the number of indices). getn is just a getter for this key (and creates it if it doesn't exist).

Since getn only provides the length of the 'array' portion of a table, we are left to our own devices to make a function which can measure the 'entire' length of the table, keys and all:

function tblLength(tbl)
  local len = 0;
  for _, _ in tbl do
    len = len + 1;
  end
  return len;
end

Looping Over a Table

Iterating (looping) over a table's contents can be done in Lua 4 using the for syntax like so (or using foreach):

t = {
  "hello",
  45,
  50
};
t['x'] = 10;

for key, value in t do
  print("[" .. key .. "]: " .. tostring(value));
end

-- you can also use the `foreach` function:
foreach(t, function (key, value)
  print("[" .. key .. "]: " .. tostring(value));
end);

Iterating over an array's indices (and excluding any other keys which may exist on the table) is done with a range based loop and getn (or foreachi):

t = {
  10,
  20,
  30
};
t['x'] = 1000;

for idx = 1, getn(t) do
  local value = t[idx];
  print("[" .. idx .. "]: " .. tostring(value));
end

-- you can also use the `foreachi` function:
foreachi(t, function (idx, value)
  print("[" .. idx .. "]: " .. tostring(value));
end);

This is fine, but not including keyed values in the 'length' of a table is somewhat confusing. You might define your own length calc function to avoid this odd design choice on Lua's part:

function tblLength(tbl)
  local len = 0;
  for _, _ in tbl do
    len = len + 1;
  end
  return len;
end

t = {
  10,
  20,
  30
};
t['x'] = 1000;

tblLength(t); -- 4

Table Key Nuances

The key lookup must match the value and type exactly:

t = {
  10,
  20,
};
t['1'] = 500;

t[1]; -- 10
t['1']; -- 500

When a table is used as a value, Lua instead returns the memory address of the table. This can be a little surprising:

t1 = {};

print(t1); -- something like 'table: 0x55b8a7287f80'

t2 = {};
-- so using a table as another table's key:
t1[t2] = 10; -- use this empty table as a key to the value 10
-- is the same as
t1["table: 0x55b8a7287f80"] = 10;

-- hence, this is nil (even though t2 is an empty table, so we might expect it to index the value 10):
t1[{}];

This is because Lua tables (along with functions and userdata) are reference types, which just mean the 'value' is not actually stored in a table variable, but rather the memory address of the table is stored (which is used by Lua to 'look up' the table). This is done as tables can be very expensive in terms of memory (as they can get quite huge), so its much cheaper to simply pass a reference to the table around in scripts.

Manipulating Tables

There are plenty of table manipulation functions out there, although Lua itself provides extremely little. Almost everything useful has been made by users. An example may be printTbl. Many mods have their own custom table utils (example).

You should also check out this guide on Lua's tag method system, which can define custom behaviors for table operations like assignment and access.