table extensions
Side: Shared (client + server)
Extensions to Lua's built-in table library. All functions are added directly to the global table and are available as soon as twinded_libs is loaded.
Functions
table.copy(t)
Deep copy a table with metatable preservation.
Returns: table
table.merge(deepMerge?, ...)
Merge multiple tables into the first one. Keys from later tables overwrite earlier ones.
| Parameter | Type | Default | Description |
|---|---|---|---|
deepMerge | boolean? | true | If true, nested tables are merged recursively |
... | table | — | Tables to merge (first table is the target) |
Returns: table (the first table, modified in place)
table.mergeAfter(...)
Sequential array merge. Appends values from all tables into the first one.
Returns: table
table.isEmpty(t)
Check if a table is nil, empty, or has type "empty".
Returns: boolean
table.count(t)
Count all elements in a table. Works on mixed tables (not just arrays).
Returns: integer
table.filter(t, filterIter, keepKeyAssociation?)
Filter table elements with a predicate function.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
filterIter | function | function(value, key, table) -> boolean |
keepKeyAssociation | boolean? | If true, preserves original keys |
Returns: table
table.map(t, func)
Apply a function to each element and return a new table.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
func | function | function(value, index, table) -> newValue |
Returns: table
table.find(t, func)
Find the first matching element. func can be a function or a direct value for equality check.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
func | function or any | Predicate or value to match |
Returns: value, key — the matching value and its key, or nil if not found.
table.includes(t, value, fromIndex?)
Search for a value in a table.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
value | any | Value to search for |
fromIndex | integer? | Start searching from this index |
Returns: boolean, key
table.slice(t, s?, e?)
Extract a sub-table by indices.
| Parameter | Type | Default | Description |
|---|---|---|---|
t | table | — | Source array |
s | integer? | 1 | Start index |
e | integer? | #t | End index |
Returns: table
table.clearForNui(t)
Deep copy a table without functions. Safe for JSON serialization and NUI messages.
Returns: table
table.isEgal(t1, t2, strict?, canMissIn1?, canMissIn2?)
Deep comparison of two tables.
| Parameter | Type | Default | Description |
|---|---|---|---|
t1 | table | — | First table |
t2 | table | — | Second table |
strict | boolean? | true | Strict type comparison |
canMissIn1 | boolean? | false | Allow keys present in t2 but missing in t1 |
canMissIn2 | boolean? | false | Allow keys present in t1 but missing in t2 |
Returns: boolean
table.extract(t, key)
Extract a value by key and remove it from the table.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
key | any | Key to extract |
Returns: any — the extracted value.
table.upsert(...)
Set or update a value at a nested path. Creates intermediate tables as needed.
table.upsert(t, {key1, key2, key3}, value)Returns: table (the root table)
table.doesKeyExist(t, ...)
Check if a nested key path exists.
local exists, value = table.doesKeyExist(t, "settings", "display", "theme")Returns: boolean, value
table.getDeep(t, keys)
Read a value at a nested key path.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
keys | table | Array of keys forming the path |
Returns: value, exists, missingKey
table.deleteDeepValue(t, keys)
Delete a value at a nested key path.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
keys | table | Array of keys forming the path |
Returns: table, boolean — the root table and whether deletion succeeded.
table.deleteAndClear(t, keys)
Delete a value at a nested path AND remove empty parent tables left behind.
| Parameter | Type | Description |
|---|---|---|
t | table | Source table |
keys | table | Array of keys forming the path |
Returns: boolean
table.addMultiLevels(...)
Create nested table levels if they don't already exist.
Returns: table (the deepest level)
Examples
Deep copy
local copy = table.copy(original)Merge tables (config with defaults)
local defaults = { theme = "dark", lang = "en", debug = false }
local userConfig = { lang = "fr" }
local merged = table.merge(defaults, userConfig)
-- { theme = "dark", lang = "fr", debug = false }Filter and map
local players = {
{ name = "John", age = 25 },
{ name = "Jane", age = 16 },
{ name = "Bob", age = 30 },
}
-- Filter adults
local adults = table.filter(players, function(p) return p.age >= 18 end)
-- Get names only
local names = table.map(adults, function(p) return p.name end)Find an element
local admin, key = table.find(users, function(u) return u.role == "admin" end)
-- Or find by direct value
local found, key = table.find(names, "John")Nested operations
local config = {}
-- Set a deeply nested value (creates intermediate tables)
table.upsert(config, {"settings", "display", "theme"}, "dark")
-- Check if the path exists
local exists, value = table.doesKeyExist(config, "settings", "display", "theme")
-- exists = true, value = "dark"
-- Read a nested value
local theme, found, missing = table.getDeep(config, {"settings", "display", "theme"})
-- Delete a nested value and clean up empty parents
table.deleteAndClear(config, {"settings", "display", "theme"})Safe NUI data
-- Remove functions before sending to NUI
local safeData = table.clearForNui(complexTable)
SendNUIMessage(safeData)
