From 2e469ba3ea43e48a473eaf0a7fef956f62479911 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Tue, 27 Jan 2026 09:00:37 +0100 Subject: [PATCH] Lua: table.select API --- client/space_lua/stdlib/table.ts | 32 +++++++++++++++- client/space_lua/stdlib/table_test.lua | 39 ++++++++++++++++++- website/API/table.md | 52 +++++++++++++++----------- 3 files changed, 99 insertions(+), 24 deletions(-) diff --git a/client/space_lua/stdlib/table.ts b/client/space_lua/stdlib/table.ts index aae05079..2f3f8b37 100644 --- a/client/space_lua/stdlib/table.ts +++ b/client/space_lua/stdlib/table.ts @@ -11,7 +11,7 @@ import { type LuaValue, luaValueToJS, } from "../runtime.ts"; -import { asyncQuickSort } from "../util.ts"; +import { asyncQuickSort, evalPromiseValues } from "../util.ts"; export const tableApi = new LuaTable({ /** @@ -99,6 +99,7 @@ export const tableApi = new LuaTable({ ), /** * Returns the keys of a table. + * Note: Space Lua specific * @param tbl - The table to get the keys from. * @returns The keys of the table. */ @@ -111,6 +112,7 @@ export const tableApi = new LuaTable({ }), /** * Checks if a table (used as an array) contains a value. + * Note: Space Lua specific * @param tbl - The table to check. * @param value - The value to check for. * @returns True if the value is in the table, false otherwise. @@ -138,6 +140,34 @@ export const tableApi = new LuaTable({ } }, ), + /** + * Returns a new table from an old one, only with selected keys + * @param tbl a Lua table or JS object + * @param keys a list of keys to select from the table, if keys[0] is a table or array, assumed to contain the keys to select + * @returns a new table with only the selected keys + */ + select: new LuaBuiltinFunction( + (sf, tbl: LuaTable | Record, ...keys: LuaValue[]) => { + // Normalize arguments + if (Array.isArray(keys[0])) { + // First argument is key array, let's unpack + keys = keys[0]; + } else if (keys[0] instanceof LuaTable) { + keys = keys[0].toJSArray(); + } + const resultTable = new LuaTable(); + const setPromises: (void | Promise)[] = []; + for (const key of keys) { + setPromises.push(resultTable.set(key, luaGet(tbl, key, null, sf))); + } + const promised = evalPromiseValues(setPromises); + if (promised instanceof Promise) { + return promised.then(() => resultTable); + } else { + return resultTable; + } + }, + ), pack: new LuaBuiltinFunction((_sf, ...args: any[]) => { const tbl = new LuaTable(); for (let i = 0; i < args.length; i++) { diff --git a/client/space_lua/stdlib/table_test.lua b/client/space_lua/stdlib/table_test.lua index 7b5e680a..dda64a1e 100644 --- a/client/space_lua/stdlib/table_test.lua +++ b/client/space_lua/stdlib/table_test.lua @@ -1,9 +1,37 @@ +function deepCompare(t1, t2) + if t1 == t2 then return true end + -- If they are the same object, return true + if type(t1) ~= "table" or type(t2) ~= "table" then return false end + -- If not both tables, return false + -- Check if both tables have the same number of keys + local t1_keys = 0 + local t2_keys = 0 + for k in pairs(t1) do + t1_keys = t1_keys + 1 + end + for k in pairs(t2) do + t2_keys = t2_keys + 1 + end + if t1_keys ~= t2_keys then return false end + + -- Recursively compare each key-value pair + for k, v in pairs(t1) do + if not deepCompare(v, t2[k]) then + return false + end + end + + return true +end + local function assertEqual(a, b) - if a ~= b then - error("Assertion failed: " .. a .. " is not equal to " .. b) + if not deepCompare(a, b) then + error("Assertion failed: " .. tostring(a) .. " is not equal to " .. tostring(b)) end end + + -- Basic table operations local t = { 1, 2, 3 } table.insert(t, 4) @@ -155,3 +183,10 @@ local idx, two = table.find(t, function(v) return v == 2 end) assertEqual(idx, 3) assertEqual(two, 2) assertEqual(table.find(t, function(v) return v == 4 end), nil) + +-- Test table.select +local tbl = {name = "Pete", age = 100, parents = {"John", "Jane"}} +assertEqual(table.select(tbl, "name"), {name = "Pete"}) +assertEqual(table.select(tbl, {"name"}), {name = "Pete"}) +assertEqual(table.select(tbl, "name", "age"), { name = "Pete", age = 100}) +assertEqual(table.select(tbl, "name", "age", "non-existing"), { name = "Pete", age = 100}) diff --git a/website/API/table.md b/website/API/table.md index f57536e3..cc9cc0ab 100644 --- a/website/API/table.md +++ b/website/API/table.md @@ -53,26 +53,6 @@ table.sort(numbers, function(a, b) return a > b end) print(table.concat(numbers, ", ")) -- prints: 9, 5, 4, 3, 1, 1 ``` -## table.keys(table) -Returns an array containing all the keys in the table. - -Example: -```lua -local person = {name = "John", age = 30, city = "New York"} -local keys = table.keys(person) -print(table.concat(keys, ", ")) -- prints: name, age, city -``` - -## table.includes(table, value) -Checks if a list-table contains a specific value. - -Example: -```lua -local fruits = {"apple", "banana", "orange"} -print(table.includes(fruits, "banana")) -- prints: true -print(table.includes(fruits, "grape")) -- prints: false -``` - ## table.pack(...) Creates a new table with the given arguments. The resulting table has all arguments stored at integer keys starting with 1, and a field "n" with the total number of arguments. @@ -97,7 +77,27 @@ local x, y = table.unpack(fruits, 2, 3) print(x, y) -- prints: banana orange ``` -# Non-standard functions +# Non-standard APIs +## table.keys(table) +Returns an array containing all the keys in the table. + +Example: +```lua +local person = {name = "John", age = 30, city = "New York"} +local keys = table.keys(person) +print(table.concat(keys, ", ")) -- prints: name, age, city +``` + +## table.includes(table, value) +Checks if a list-table contains a specific value. + +Example: +```lua +local fruits = {"apple", "banana", "orange"} +print(table.includes(fruits, "banana")) -- prints: true +print(table.includes(fruits, "grape")) -- prints: false +``` + ## table.find(table, criteriaFn, fromIndex?) Finds an element in a table that matches a criteria function. Returns a Lua multi value of index and first element or nil if no element is found. @@ -115,3 +115,13 @@ print(firstEvenAfter3) -- prints: 4 local result = table.find(numbers, function(n) return n > 10 end) print(result) -- prints: nil ``` + +## table.select(table, keys...) +Returns a new table from an old one, only with selected keys. + +Example: +${query[[ + from p = index.tag "page" + limit 3 + select table.select(p, "name", "lastModified") +]]} \ No newline at end of file