Refactor table.lua

Reduce Code Duplication
Make table.equals work with nested tables
Add/Improve assert() for complex functions
Add table.match to improve table.search
master
benrob0329 2020-01-17 16:33:08 -05:00
parent b6640b32e5
commit 7b70895ea5
1 changed files with 66 additions and 45 deletions

View File

@ -1,17 +1,36 @@
function table.equals(a, b)
for k, v in pairs(a) do
if not (b[k] and b[k] == v) then
local t2s = minetest.serialize
local error_message = [[
Function %s does not pass self-test!
Expected results: %s
Test Results: %s
]]
local function half_equals(t1, t2)
for k, v in pairs(t1) do
if not t2[k] then
return false
end
end
for k, v in pairs(b) do
if not (a[k] and a[k] == v) then
elseif (type(v) == "table") then
if not half_equals(t2[k], v) then
return false
end
elseif not (t2[k] == v) then
return false
end
end
return true
end
function table.equals(t1, t2)
if half_equals(t1, t2) and half_equals(t2, t1) then
return true
else
return false
end
end
assert(table.equals({"a", "b", c = "d", e = {"f", "g", "h", {"h"}}}, {"a", "b", c = "d", e = {"f", "g", "h", {"h"}}}))
assert(not table.equals({a = 1}, {a = 2}))
function table.shuffle(t)
for i = #t, 2, -1 do
local j = math.random(i)
@ -28,13 +47,41 @@ end
function table.is_in(t, value)
for _, v in ipairs(t) do
if v == value then
if (type(value) == "table") and table.equals(t, value) then
return true
elseif v == value then
return true
end
end
return false
end
-- Returns matching values between two tables
function table.match(t1, t2)
local results = {}
for k, v in pairs(t2) do
if (type(k) == "number") and table.is_in(t1, v) then
table.insert(results, v)
elseif not t1[k] then
break
elseif type(v) == "table" then
local tmp_tbl = table.match(t1[k], v)
if not table.equals(tmp_tbl, {}) then
results[k] = tmp_tbl
end
elseif t1[k] == t2[k] then
results[k] = v
end
end
return results
end
local match_test_table = {"foo", "bar", bar = {1, 2, 3}, baz = "boo", foo = {"baz"}}
local match_expected_results = {"foo", bar = {1, 3}, baz = "boo"}
local match_test_results = table.match(match_test_table, {"foo", bar = {1, 3}, baz = "boo", "furb", foo = {"bar"}})
local match_error_message = error_message:format("table.match", t2s(match_expected_results), t2s(match_test_results))
assert(table.equals(match_expected_results, match_test_results), match_error_message)
-- Returns a sub-set of an indexed table which matches the given parameters
function table.search(t, params)
params = params or {}
@ -43,30 +90,10 @@ function table.search(t, params)
local results = {}
for _, entree in ipairs(t) do
local should_include = true
for key, value in pairs(params.includes) do
if type(value) == "table" then
for _, v in ipairs(value) do
if (not entree[key]) or (not table.is_in(entree[key], v)) then
should_include = false
end
end
elseif (entree[key] == nil) or (entree[key] ~= value) then
should_include = false
end
end
for key, value in pairs(params.excludes) do
if type(value) == "table" then
for _, v in ipairs(value) do
if entree[key] and (table.is_in(entree[key], v)) then
should_include = false
end
end
elseif entree[key] and entree[key] == value then
should_include = false
end
end
if should_include then
local includes_match = table.match(entree, params.includes)
local excludes_match = table.match(entree, params.excludes)
if table.equals(includes_match, params.includes) and table.equals(excludes_match, {}) then
table.insert(results, entree)
end
end
@ -74,7 +101,7 @@ function table.search(t, params)
return results
end
local test_table = {
local search_test_table = {
{tags = {"a", "b", "c"}, num = 1, num2 = 3},
{tags = {"b", "c"}, num = 1, num2 = 4},
{tags = {"b"}, num = 2, num2 = 3},
@ -82,15 +109,9 @@ local test_table = {
{tags = {"a", "b"}, num = 1, num2 = 3},
}
local expected_results = {test_table[4]}
local test_results = table.search(test_table, {includes = {tags = {"b"}, num = 1}, excludes = {tags = {"a"}, num2 = 4}})
local error_message = [[
Function table.search does not pass self-test!
Expected results: %s
Test Results: %s
]]
error_message:format(minetest.serialize(expected_results), minetest.serialize(test_results))
assert(table.equals(test_results, expected_results), error_message)
local search_expected_results = {search_test_table[4]}
local search_test_results = table.search(search_test_table,
{includes = {tags = {"b"}, num = 1}, excludes = {tags = {"a"}, num2 = 4}})
local search_error_message =
error_message:format("table.search", t2s(search_expected_results), t2s(search_test_results))
assert(table.equals(search_test_results, search_expected_results), search_error_message)