# `table` `a` keeps Lua's standard `table` library (`insert`, `remove`, `concat`, `sort`, `pack`, `unpack`) and adds the higher-order and collection helpers stock Lua leaves out: map/filter/reduce, merge and deep-copy, aggregates, lookups, and a read-only proxy. The additions live on the same global `table`. ```lua local nums = {1, 2, 3, 4, 5} local evens = table.ifilter(nums, function(n) return n % 2 == 0 end) local doubled = table.map(nums, function(n) return n * 2 end) print(table.sum(nums), table.max(nums)) --> 15 5 ``` ## Sequences vs. general tables Lua tables are both arrays and maps, so most helpers come in two flavours. The distinction runs through the whole module: - **Plain name** (`map`, `filter`, `reduce`) iterates with `pairs` — it visits **every** key, preserves keys in the result, and the order is unspecified. - **`i`-prefixed name** (`imap`-style: `ifilter`, `ireduce`, …) iterates with `ipairs` over the sequence part `1..#t` **in order**, and produces a fresh gapless sequence. Reach for the `i` variants when you have an array and want array semantics (order preserved, no gaps); use the plain ones for maps or when keys matter. ## Transforming ### `table.map(tbl, fn)` Apply `fn(value)` to every value, **preserving keys**. Order unspecified. ```lua table.map({a = 1, b = 2}, function(v) return v * 10 end) --> {a = 10, b = 20} ``` ### `table.filter(tbl, predicate)` Keep the entries where `predicate(value)` is truthy, **preserving keys**. On a sequence this may leave gaps — use `ifilter` to avoid them. ```lua table.filter({a = 1, b = 2, c = 3}, function(v) return v > 1 end) --> {b = 2, c = 3} ``` ### `table.ifilter(tbl, predicate)` Keep the sequence elements matching `predicate`, producing a new **gapless** sequence in order. ```lua table.ifilter({1, 2, 3, 4}, function(v) return v % 2 == 0 end) --> {2, 4} ``` ### `table.filterMap(tbl, fn)` Map and filter in one pass: apply `fn(value)`, and **drop entries where `fn` returns `nil`**. Keys are preserved (may leave gaps on a sequence). ```lua -- keep and square only the even numbers table.filterMap({a = 1, b = 2, c = 4}, function(v) if v % 2 == 0 then return v * v end end) --> {b = 4, c = 16} ``` ### `table.ifilterMap(tbl, fn)` The sequence version of `filterMap`: map over `1..#t` in order, drop `nil` results, and return a **gapless** sequence. ```lua table.ifilterMap({1, 2, 3, 4}, function(v) if v % 2 == 0 then return v * 10 end end) --> {20, 40} ``` ## Reducing ### `table.reduce(tbl, fn, init)` Fold every value into a single accumulator: `acc = fn(acc, value)`, starting from `init`. Iterates with `pairs`, so order is unspecified — use it for order-independent folds (sum, product, building a set). ```lua table.reduce({1, 2, 3}, function(acc, v) return acc + v end, 0) --> 6 ``` ### `table.ireduce(tbl, fn, init)` Like `reduce` but folds the sequence part **in order**, for when order matters (e.g. concatenation, left-to-right composition). ```lua table.ireduce({"a", "b", "c"}, function(acc, v) return acc .. v end, "") --> "abc" ``` ## Aggregates These walk all values with `pairs`, so they work on sequences and maps alike. Each returns `nil` for an empty table (except `sum`, which returns `0`), and a `NaN` value anywhere propagates to the result. | Function | Result | |--------------------|-----------------------------------------| | `table.min(tbl)` | smallest value, or `nil` if empty | | `table.max(tbl)` | largest value, or `nil` if empty | | `table.mean(tbl)` | arithmetic mean, or `nil` if empty | | `table.sum(tbl)` | sum of values, `0` if empty | ```lua local t = {4, 8, 15, 16, 23, 42} print(table.min(t), table.max(t)) --> 4 42 print(table.sum(t), table.mean(t)) --> 108 18.0 ``` ## Querying ### `table.keys(tbl)` / `table.values(tbl)` Return an array of all keys, or all values. Order is unspecified. ```lua table.keys({a = 1, b = 2}) --> {"a", "b"} (some order) table.values({a = 1, b = 2}) --> {1, 2} (matching order) ``` ### `table.contains(tbl, value)` Return `true` if any value equals `value` (compared with `==`). ```lua table.contains({"red", "green"}, "green") --> true ``` ### `table.find(tbl, predicate)` Return the **first** value matching `predicate`, together with its key: `(value, key)`, or `(nil, nil)` if none match. Because a matching value could itself be `false`, test the returned **key** against `nil` to distinguish "found `false`" from "not found". ```lua local v, k = table.find({10, 20, 30}, function(x) return x > 15 end) print(v, k) --> 20 2 ``` ### `table.isEmpty(tbl)` Return `true` if the table has no keys at all (faster and more correct than `#tbl == 0`, which only inspects the sequence part). ```lua table.isEmpty({}) --> true table.isEmpty({x = 1}) --> false ``` ## Combining and copying ### `table.merge(...)` Merge any number of tables into a **new** table (a shallow merge). Sequence parts are **appended** in argument order; other keys are copied, with later arguments overwriting earlier ones on a collision. Not recursive — for nested merges use `deepMerge`. ```lua table.merge({1, 2}, {3, 4}) --> {1, 2, 3, 4} (sequences concatenate) table.merge({a = 1, b = 2}, {b = 9}) --> {a = 1, b = 9} (later wins) ``` ### `table.deepMerge(...)` Like `merge`, but where the same key holds a table on both sides, those tables are merged **recursively**. The result shares no tables with the inputs — everything is deep-copied — so mutating the result never touches the originals. ```lua table.deepMerge( { db = { host = "localhost", port = 5432 } }, { db = { port = 5433 } } ) --> { db = { host = "localhost", port = 5433 } } ``` ### `table.deepCopy(value)` Return a deep copy of a table, recursively copying nested tables. Metatables are **not** copied. Non-table values pass through unchanged. ```lua local original = { list = {1, 2, 3} } local copy = table.deepCopy(original) copy.list[1] = 99 print(original.list[1]) --> 1 (unaffected) ``` `deepMerge` and `deepCopy` cap recursion at 100 levels and error beyond that, so a cyclic table raises rather than looping forever. ### `table.reverse(tbl)` Return a new array with the sequence elements in reverse order. ```lua table.reverse({1, 2, 3}) --> {3, 2, 1} ``` ## Protecting ### `table.readonly(tbl [, name])` Return a read-only proxy of `tbl`: reads pass through to the underlying table, writes raise an error. The optional `name` is included in that error message. Useful for exposing a constant namespace that callers must not mutate. ```lua local config = table.readonly({ retries = 3 }, "config") print(config.retries) --> 3 config.retries = 5 --> error: Attempt to update a read-only table config ``` **Caveat:** the proxy is an empty shell that forwards reads through a metatable, so `pairs`, `next`, and `#` see **no** contents — it is meant for guarding API tables you read by known key, not for iterable data. ## Notes - **Additions, not replacements.** Stock `table.insert`, `remove`, `concat`, `sort`, `pack`, `unpack` are all still present. - **Map vs. sequence is the recurring choice.** Plain functions preserve keys via `pairs` (unspecified order); `i`-prefixed ones produce gapless sequences via `ipairs` (in order). Pick by whether you have an array or a map. - **Most helpers return new tables.** `map`, `filter*`, `merge*`, `deepCopy`, `reverse`, `keys`, `values` never mutate their input. - **Type-checked.** Passing a non-table where a table is expected raises a clear error naming the offending function.