Lua lacks some very useful built in functionality to tables that plenty of other languages are able to take advantage of. The table
… table that provides additional functionality to tables is quite limited and really only applies to array-like tables. Also this table is readonly, which means I had to make a whole class for this. Fortunately due to metatables, tables are able to be indexed as normal.
The asset can be found here.
The github can be found here. The github is the same as the asset, but it also has the test file, which I used Nexus unit testing to test the functionality to ensure it works. If you PR something, I advise you to add a unit test to it.
Documentation
Index
Index
- Creating a new table
- Indexing a table
- Getting the length of a table
- Inserting values into a table
- Removing values from a table
- Iterating a table
- Sorting array tables
- Finding an element in a table
- Finding an index/key of a value in a table
- Creating a copy
- Filling a table
- Testing values in the table
- Removing all values that don’t pass a predicate function
- Concatenating two tables together
- Shuffling an array
Creating a new table
Because the table table is readonly, functionality was extended by creating a new class. You can create a BetterTable in a few ways:
Creating an empty table
If you wish to create an empty table, just call the new
function. This will be an empty table, but with the BetterTable functionality on it.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
Creating a BetterTable from an existing table
You can convert existing tables (and even existing BetterTables) into BetterTables by passing the table into the new
function.
- NOTE: All tables are deep cloned, so it isn’t recommended to do this if you have a reference to something inside the table from outside the table, they won’t retain the same reference.
local BetterTables = require(path.to.BetterTables)
local tbl = {1,2,3,4,5}
local t = BetterTables.new(tbl)
Creating an array table with a specific size initializer
You can create a table with a size initialization as well. This is useful because resizing tables dynamically can be expensive. Additionally, you can pass an initial value which will be filled into all of the newly created slots
local BetterTables = require(path.to.BetterTables)
local size = 10
local value = "Hello!"
local t = BetterTables.new(size, value)
Indexing the table
BetterTables can be indexed identically to regular tables using []
or .
.
local BetterTables = require(path.to.BetterTables)
local size = 10
local value = "Hello!"
local t = BetterTables.new(size, value)
print(t[1]) -- "Hello!"
– OR –
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new({["Hello"]="Goodbye!"})
print(t.Hello) -- "Goodbye!"
print(t["Hello"]) -- "Goodbye!"
Getting the length of the table
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new(10, "a")
print(t:GetLength()) -- 10
- NOTE: GetLength() works with all types of tables.
Inserting values into a table
Unfortunately because table
is readonly, all of the table
methods are implemented as functions on the table itself.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert("Hello!")
print(t[1]) -- "Hello!"
- NOTE:
Insert
usestable.insert
, so the underlying functionality is identical. It supports inserting at specific indices usingInsert(pos, value)
Removing values from a table
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert("Hello!")
print(t[1]) -- "Hello!"
t:Remove(1)
print(t[1]) -- nil
- NOTE:
Remove
uses table.remove so its underlying functionality is identical.
Iterating a table
You can iterate over any type of table, however the type of iteration is different depending on the type. For array tables, ipairs
iteration is returned, for mixed and dictionary tables pairs
is returned. ipairs
essentially maintains the order of iteration so that index 1 is the first value and index #table
is the last value. ipairs
and pairs
are not metamethods that Luau has, unfortunately, so this is implemented as a method.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert("Hello!")
t:Insert("Goodbye!")
for i, v in t:GetIterator() do
print(i, v) -- "1 Hello!" then "2 Goodbye!"
end
Sorting array tables
You can sort array tables because the order in which they are retrieved when iterating remains the same, which is unlike dictionaries which can return in any order.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert(2)
t:Insert(1)
t:Sort() -- default sort is ascending
print(t[1]) -- 1
print(t[2]) -- 2
- NOTE: Attempting to sort a non-array table will result in an error
Finding an element in a table
table.find
works a little differently than find
functions in other languages. I’ve implemented it to be like those other languages which allow you to pass a predicate function to test values against. This allows for much more control over the result.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
local v = 7
t:Insert(v)
for i = 1, 100 do
t:Insert(math.random(0,5))
end
local val = t:Find(function(value, index, tbl)
if value > 5 then
return true
end
return false
end)
print(val) -- 7
The function you pass must return true or false. The arguments it can accept are value
, which is the current iteration’s value, key
which is the current iteration’s key (or index), and table
which is the BetterTable itself. You don’t need to accept all of these arguments, but they are always passed in the order value, key, table
.
- NOTE:
Find
works on all types of tables - NOTE: If no value passes the function
nil
is returned
Finding an index/key of a value in a table
Like table.find
, you can also find a specific index of a value in a table. FindKey
and FindIndex
are identical, they only have a name change to respect the fact that array tables’ keys are indices. FindKey
(and FindIndex
) may be passed a single value, in which case a simple equivalency check is made, or a function that is identical to the one in Find
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert(3)
t:Insert(6)
t:Insert(8)
t:Insert(9)
t:Insert(4)
t:Insert(7)
local index = t:FindIndex(8)
print(index) -- 3
– OR –
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert(3)
t:Insert(6)
t:Insert(8)
t:Insert(9)
t:Insert(4)
t:Insert(7)
local index = t:FindIndex(function(value, key, tbl)
if value > 8 then
return true
end
return false
end)
print(index) -- 4
- NOTE:
FindKey
andFindIndex
are identical and have the exact same functionality and can be used on any type of table.
Creating a copy
- NOTE: Both copy methods return regular tables and not BetterTables.
Creating a deep copy
Deep copies of tables allow you to get a brand new table with nothing being equivalent to anything in the table it was cloned from. This means tables within tables will not hold the same reference in memory.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
local x = {5}
t:Insert(x)
local newTable = t:DeepCopy()
print(x == newTable[1]) -- false
Creating a shallow copy
Unlike a deep copy, a shallow copy retains all references in which means tables in tables remain the same in memory.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
local x = {5}
t:Insert(x)
local newTable = t:ShallowCopy()
print(x == newTable[1]) -- true
Filling a table
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
for i = 1, 5 do
t:Insert("Yay!")
end
print(t[5]) -- "Yay!"
t:Fill("F", 5, 10)
for i = 5, 10 do
print(t[i]) -- "F" (x6)
end
- NOTE: Fill takes 3 arguments:
value
,fromIndex
, andtoIndex
. Both indices are inclusive.fromIndex
is optional, and is defaulted at0
Testing values in the table
Other languages provide ways to test values in a table and return true
or false
. Luau has table.foreach
, however it is a void
function. BetterTables allows you to pass a function to test against values in the table. The arguments it can accept are value
, which is the current iteration’s value, key
which is the current iteration’s key (or index), and table
which is the BetterTable itself. You don’t need to accept all of these arguments, but they are always passed in the order value, key, table
- NOTE: This works on any type of table
Testing every value
Every
tests whether every value in the table passes the test function passed.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
for i = 1, 100 do
t:Insert(math.random(6,100))
end
local everyValueGreaterThan5 = t:Every(function(value)
if value > 5 then return true end
return false
end)
print(everyValueGreaterThan5) -- true
Testing any value
Some
tests whether any value passes the test function passed.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
for i = 1, 100 do
t:Insert(math.random(0,5))
end
t:Insert(6)
local someValueGreaterThan5 = t:Some(function(value)
if value > 5 then return true end
return false
end)
print(someValueGreaterThan5) -- true
Removing all values that don’t pass a predicate function
- NOTE: This change happens in place meaning it changes the contents of the table and does not return a new one.
- NOTE: The tester function’s arguments it can accept are
value
, which is the current iteration’s value,key
which is the current iteration’s key (or index), andtable
which is the BetterTable itself. You don’t need to accept all of these arguments, but they are always passed in the ordervalue, key, table
. - NOTE: The tester function should return
true
if the value should be kept in the table.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
for i = 1, 10 do
t:Insert(5)
end
print(t[1]) -- 5
t:Insert(3)
t:Filter(function(value)
return value ~= 5
end)
print(t[1]) -- 3
Concatenating two tables together
You can combine two tables using Concat
. This function combines the two tables.
- NOTE: If both of the tables are array-like, new values are added to the end of the table.
- NOTE: If either table is not array-like, new values will override old values if conflicting keys exist.
- NOTE:
Concat
accepts both BetterTables and regular tables.
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert(3)
local t2 = {5}
t:Concat(t2)
print(t[1]) -- 3
print(t[2]) -- 5
Shuffling an array
local BetterTables = require(path.to.BetterTables)
local t = BetterTables.new()
t:Insert(3)
t:Insert(5)
t:Shuffle()
- NOTE: Shuffle is done in place.
- NOTE: Value and index cannot be guaranteed.
- NOTE: Attempting to shuffle a non-array table will result in an error
Feel free to request new methods you think would be useful!