What are tables?
Tables are key/value pairs, designed for fast lookup of the value corresponding to a particular key.
What data can you put in tables?
Any Lua types, excepting the special "non-value" nil, can be used for either the key or the value part of tables.
tprint function
For illustrative purposes I will be using the "tprint" function I wrote a while ago that recursively shows the contents of a table. This function ships with MUSHclient in the tprint.lua file. To use it, simply put this line near the start of your script:
Table constructors
Before you can use a table it must be "constructed" using a variant of the syntax:
The above constructs an empty table and puts a reference to it in variable "t".
Immediately we notice something interesting, the order in which items are retrieved from a table is not necessarily the order in which they were inserted, nor alphabetic order either. Since tables are stored internally using "hashing" algorithms there is no guarantee of which order items are retrieved, when doing a linear scan. The exception to this is using numeric keys to simulate a vector, described further on.
The above example can be rewritten without the quotes and square brackets, as a form of shorthand, which has the same result, if you wish to key by string keys:
The shorthand method shown above, where you omit the square brackets, only applies if the key is a valid Lua dataname (ie. starting with a letter or underscore, and then consisting of letters, numbers and underscores).
You can also use any other data type for keys, except nil. Usually this means using numeric keys, but you are not restricted to that.
Note an important point here, the item keyed by the number 10 is a different item to that keyed by the string "10".
You can let Lua assign numeric keys for you, especially if you are trying to set up an "ordered" table, like this:
Here Lua has assigned a numeric key to each item, starting at 1.
You can use variables as keys in which case the underlying value is used:
An important distinction is between these two forms:
Accessing table elements
Once your table has been created you can access individual elements by referring to their key:
Once again you refer to the key by putting it in square brackets (and then quoting it if it is a string), however as a shorthand you can simply have tablename.key providing the key is a valid Lua identifier (as in the case of t.name above).
Changing table elements
You can change individual items, or add more, using similar syntax:
The above example adds a new element (t.race) by simply assigning to it. If the entry does not exist it is created.
Deleting table elements
You delete a table item by assigning nil to it:
Scanning through a table
Often you want to find everything in a table. There are two main ways of doing this. The simplest is to use table.foreach, like this:
The function table.foreach iterates over a table, calling the supplied function with two arguments for each table entry, the corresponding key and value. You can write your own function of course:
You have the option in your function of returning a non-nil value based on some decision, in which case the iterating of the table stops, and the value you supply is returned as the return value of the foreach function...
The other main way of iterating through a table is to use the "for ... pairs" construct, like this:
This method is a bit more flexible as it lets you do things inside your loop (like recursively loop like the tprint function does).
There are variants on these two methods ('table.foreachi', and 'for ... ipairs') which are discussed further down under the treatment of ordered tables (vectors).
Tables can contain tables
Since we have seen that tables can hold any data type, except nil, then clearly tables can themselves consist of sub-tables.
The tprint function helps to see the nested relationship by indenting sub-tables.
Tables are anonymous and stored by reference
A table is not tied to a particular dataname (variable). For a start, you can have tables without any name:
In the above example we are printing a table constructed on-the-fly which has no name.
Here we copied the reference to the table from t1 to t2, however they both point to the same table. Hence when I changed t2.food to "fish", it also got changed in t1.
Deleting tables
You do not explicitly delete tables. Once there are no references to a table the Lua garbage-collector will eventually delete it.
Using foreach to find a particular entry
You can use a function combined with foreach to do a linear search of a table until a certain condition is satisfied. Here, let's find which race has a wisdom of 25 (the first one, anyway) ...
Tables are key/value pairs, designed for fast lookup of the value corresponding to a particular key.
What data can you put in tables?
Any Lua types, excepting the special "non-value" nil, can be used for either the key or the value part of tables.
tprint function
For illustrative purposes I will be using the "tprint" function I wrote a while ago that recursively shows the contents of a table. This function ships with MUSHclient in the tprint.lua file. To use it, simply put this line near the start of your script:
require "tprint"
Table constructors
Before you can use a table it must be "constructed" using a variant of the syntax:
t = { }
The above constructs an empty table and puts a reference to it in variable "t".
t = {
["name"] = "nick",
["class"] = "mage",
["hp"] = 234,
["online"] = true
}
tprint (t)
Output
"online"=true
"name"="nick"
"class"="mage"
"hp"=234
Immediately we notice something interesting, the order in which items are retrieved from a table is not necessarily the order in which they were inserted, nor alphabetic order either. Since tables are stored internally using "hashing" algorithms there is no guarantee of which order items are retrieved, when doing a linear scan. The exception to this is using numeric keys to simulate a vector, described further on.
The above example can be rewritten without the quotes and square brackets, as a form of shorthand, which has the same result, if you wish to key by string keys:
t = {
name = "nick",
class = "mage",
hp = 234,
online = true
}
tprint (t)
Output
"online"=true
"name"="nick"
"class"="mage"
"hp"=234
The shorthand method shown above, where you omit the square brackets, only applies if the key is a valid Lua dataname (ie. starting with a letter or underscore, and then consisting of letters, numbers and underscores).
You can also use any other data type for keys, except nil. Usually this means using numeric keys, but you are not restricted to that.
t = {
[10] = "number 10",
["10"] = "string 10",
[true] = "a true key",
[print] = "keyed by a function"
}
tprint (t)
Output
10="number 10"
true="a true key"
function: 00DD6020="keyed by a function"
"10"="string 10"
Note an important point here, the item keyed by the number 10 is a different item to that keyed by the string "10".
You can let Lua assign numeric keys for you, especially if you are trying to set up an "ordered" table, like this:
t = {
"the",
"quick",
"brown",
"fox",
"jumped"
}
tprint (t)
Output
1="the"
2="quick"
3="brown"
4="fox"
5="jumped"
Here Lua has assigned a numeric key to each item, starting at 1.
You can use variables as keys in which case the underlying value is used:
a = "nick"
b = 22
c = print
t = {
[a] = "fish",
[b] = "mage",
[c] = 234,
}
tprint (t)
Output
function: 00DD3070=234
"nick"="fish"
22="mage"
An important distinction is between these two forms:
t [a] = "something" --> indexed by whatever variable 'a' contains
t ["a"] = "something else" --> indexed literally by the string "a"
Accessing table elements
Once your table has been created you can access individual elements by referring to their key:
t = {
["name"] = "nick",
["class"] = "mage",
["hp"] = 234,
["online"] = true,
[99] = "a number"
}
print (t.name) --> nick
print (t ["class"]) --> mage
print (t [99]) --> a number
Once again you refer to the key by putting it in square brackets (and then quoting it if it is a string), however as a shorthand you can simply have tablename.key providing the key is a valid Lua identifier (as in the case of t.name above).
Changing table elements
You can change individual items, or add more, using similar syntax:
t = {
["name"] = "nick",
["class"] = "mage",
["hp"] = 234,
["online"] = true,
[99] = "a number"
}
t.name = "john"
t ["class"] = "warrior"
t [99] = 88
t.race = "dwarf"
tprint (t)
Output
"online"=true
"name"="john"
99=88
"class"="warrior"
"hp"=234
"race"="dwarf"
The above example adds a new element (t.race) by simply assigning to it. If the entry does not exist it is created.
Deleting table elements
You delete a table item by assigning nil to it:
t = {
["name"] = "nick",
["class"] = "mage",
["hp"] = 234,
["online"] = true,
[99] = "a number"
}
t.name = nil
t ["class"] = nil
tprint (t)
Output
"online"=true
99="a number"
"hp"=234
Scanning through a table
Often you want to find everything in a table. There are two main ways of doing this. The simplest is to use table.foreach, like this:
t = {
name = "nick",
class = "mage",
hp = 234,
online = true
}
table.foreach (t, print)
Output
online true
name nick
class mage
hp 234
The function table.foreach iterates over a table, calling the supplied function with two arguments for each table entry, the corresponding key and value. You can write your own function of course:
t = {
name = "nick",
class = "mage",
hp = 234,
online = true
}
function f (k, v)
print ("key = " .. tostring (k) ..
", value = " .. tostring (v))
end -- f
table.foreach (t, f)
Output
key = online, value = true
key = name, value = nick
key = class, value = mage
key = hp, value = 234
You have the option in your function of returning a non-nil value based on some decision, in which case the iterating of the table stops, and the value you supply is returned as the return value of the foreach function...
t = {
name = "nick",
class = "mage",
hp = 234,
online = true
}
function f (k, v)
print ("key = " .. tostring (k) .. ", value = " .. tostring (v))
if v == "nick" then
return true
end -- if
end -- f
table.foreach (t, f)
Output
key = online, value = true
key = name, value = nick
The other main way of iterating through a table is to use the "for ... pairs" construct, like this:
t = {
name = "nick",
class = "mage",
hp = 234,
online = true
}
for k, v in pairs (t) do
print (k, v)
end -- for loop
Output
online true
name nick
class mage
hp 234
This method is a bit more flexible as it lets you do things inside your loop (like recursively loop like the tprint function does).
There are variants on these two methods ('table.foreachi', and 'for ... ipairs') which are discussed further down under the treatment of ordered tables (vectors).
Tables can contain tables
Since we have seen that tables can hold any data type, except nil, then clearly tables can themselves consist of sub-tables.
t = {
dwarf = { str = 22, dex = 23, wis = 18 },
human = { str = 20, dex = 20, wis = 20 },
elf = { str = 18, dex = 24, wis = 25 },
}
tprint (t)
Output
"dwarf":
"str"=22
"dex"=23
"wis"=18
"human":
"str"=20
"dex"=20
"wis"=20
"elf":
"str"=18
"dex"=24
"wis"=25
The tprint function helps to see the nested relationship by indenting sub-tables.
Tables are anonymous and stored by reference
A table is not tied to a particular dataname (variable). For a start, you can have tables without any name:
tprint ( { "the", "quick", food = "eggs" } )
tprint (t)
Output
1="the"
2="quick"
"food"="eggs"
In the above example we are printing a table constructed on-the-fly which has no name.
t1 = { "the", "quick", food = "eggs" }
t2 = t1 --> copy reference to table
t2.food = "fish"
tprint (t1)
print "----"
tprint (t2)
Output
1="the"
2="quick"
"food"="fish"
----
1="the"
2="quick"
"food"="fish"
Here we copied the reference to the table from t1 to t2, however they both point to the same table. Hence when I changed t2.food to "fish", it also got changed in t1.
Deleting tables
You do not explicitly delete tables. Once there are no references to a table the Lua garbage-collector will eventually delete it.
Using foreach to find a particular entry
You can use a function combined with foreach to do a linear search of a table until a certain condition is satisfied. Here, let's find which race has a wisdom of 25 (the first one, anyway) ...
t = {
dwarf = { str = 22, dex = 23, wis = 18 },
human = { str = 20, dex = 20, wis = 20 },
elf = { str = 18, dex = 24, wis = 25 },
}
print (table.foreach (t,
function (k, v)
if v.wis == 25 then
return k
end -- if
end -- function
))
Output
elf