mirror of
https://github.com/1bardesign/batteries.git
synced 2024-11-22 22:24:35 +00:00
Merge pull request #52 from idbrii/table-deep-test
tablex: Add deep & shallow for copy, overlay; remove redundant recursion; add tests
This commit is contained in:
commit
461c3a8131
27
.github/workflows/testy.yml
vendored
Normal file
27
.github/workflows/testy.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
testy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
# tests.lua expects top level folder to be 'batteries'
|
||||||
|
path: 'batteries'
|
||||||
|
- name: Setup Lua
|
||||||
|
uses: leafo/gh-actions-lua@v8
|
||||||
|
with:
|
||||||
|
luaVersion: 5.4
|
||||||
|
- name: Setup Lua Rocks
|
||||||
|
uses: leafo/gh-actions-luarocks@v4
|
||||||
|
- name: Setup testy
|
||||||
|
# Install from github because rock is 6+ years old.
|
||||||
|
run: luarocks install https://raw.githubusercontent.com/siffiejoe/lua-testy/master/testy-scm-0.rockspec
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd batteries
|
||||||
|
testy.lua tests.lua
|
8
init.lua
8
init.lua
@ -64,16 +64,16 @@ function _batteries:export()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--overlay tablex and functional and sort routines onto table
|
--overlay tablex and functional and sort routines onto table
|
||||||
self.tablex.overlay(table, self.tablex)
|
self.tablex.shallow_overlay(table, self.tablex)
|
||||||
--now we can use it through table directly
|
--now we can use it through table directly
|
||||||
table.overlay(table, self.functional)
|
table.shallow_overlay(table, self.functional)
|
||||||
self.sort:export()
|
self.sort:export()
|
||||||
|
|
||||||
--overlay onto global math table
|
--overlay onto global math table
|
||||||
table.overlay(math, self.mathx)
|
table.shallow_overlay(math, self.mathx)
|
||||||
|
|
||||||
--overlay onto string
|
--overlay onto string
|
||||||
table.overlay(string, self.stringx)
|
table.shallow_overlay(string, self.stringx)
|
||||||
|
|
||||||
--overwrite assert wholesale (it's compatible)
|
--overwrite assert wholesale (it's compatible)
|
||||||
assert = self.assert
|
assert = self.assert
|
||||||
|
@ -44,8 +44,10 @@ for _, v in ipairs({
|
|||||||
"dedupe",
|
"dedupe",
|
||||||
"collapse",
|
"collapse",
|
||||||
"append",
|
"append",
|
||||||
"overlay",
|
"shallow_overlay",
|
||||||
"copy",
|
"deep_overlay",
|
||||||
|
"shallow_copy",
|
||||||
|
"deep_copy",
|
||||||
}) do
|
}) do
|
||||||
local table_f = table[v]
|
local table_f = table[v]
|
||||||
sequence[v] = function(self, ...)
|
sequence[v] = function(self, ...)
|
||||||
|
120
tablex.lua
120
tablex.lua
@ -328,61 +328,80 @@ if not tablex.clear then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--note:
|
-- Copy a table
|
||||||
-- copies and overlays are currently not satisfactory
|
-- See shallow_overlay to shallow copy into an existing table to avoid garbage.
|
||||||
--
|
function tablex.shallow_copy(t)
|
||||||
-- i feel that copy especially tries to do too much and
|
if type(t) == "table" then
|
||||||
-- probably they should be split into separate functions
|
local into = {}
|
||||||
-- to be both more explicit and performant, ie
|
|
||||||
--
|
|
||||||
-- shallow_copy, deep_copy, shallow_overlay, deep_overlay
|
|
||||||
--
|
|
||||||
-- input is welcome on this :)
|
|
||||||
|
|
||||||
--copy a table
|
|
||||||
-- deep_or_into is either:
|
|
||||||
-- a boolean value, used as deep flag directly
|
|
||||||
-- or a table to copy into, which implies a deep copy
|
|
||||||
-- if deep specified:
|
|
||||||
-- calls copy method of member directly if it exists
|
|
||||||
-- and recurses into all "normal" table children
|
|
||||||
-- if into specified, copies into that table
|
|
||||||
-- but doesn't clear anything out
|
|
||||||
-- (useful for deep overlays and avoiding garbage)
|
|
||||||
function tablex.copy(t, deep_or_into)
|
|
||||||
assert:type(t, "table", "tablex.copy - t", 1)
|
|
||||||
local is_bool = type(deep_or_into) == "boolean"
|
|
||||||
local is_table = type(deep_or_into) == "table"
|
|
||||||
|
|
||||||
local deep = is_bool and deep_or_into or is_table
|
|
||||||
local into = is_table and deep_or_into or {}
|
|
||||||
for k, v in pairs(t) do
|
for k, v in pairs(t) do
|
||||||
if deep and type(v) == "table" then
|
|
||||||
if type(v.copy) == "function" then
|
|
||||||
v = v:copy()
|
|
||||||
else
|
|
||||||
v = tablex.copy(v, deep)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
into[k] = v
|
into[k] = v
|
||||||
end
|
end
|
||||||
return into
|
return into
|
||||||
|
end
|
||||||
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
--overlay tables directly onto one another, shallow only
|
local function deep_copy(t, copied)
|
||||||
--takes as many tables as required,
|
local clone = t
|
||||||
--overlays them in passed order onto the first,
|
if type(t) == "table" then
|
||||||
--and returns the first table with the overlay(s) applied
|
if copied[t] then
|
||||||
function tablex.overlay(a, b, ...)
|
clone = copied[t]
|
||||||
assert:type(a, "table", "tablex.overlay - a", 1)
|
elseif type(t.copy) == "function" then
|
||||||
assert:type(b, "table", "tablex.overlay - b", 1)
|
clone = t:copy()
|
||||||
for k,v in pairs(b) do
|
assert:type(clone, "table", "copy() didn't return a copy")
|
||||||
a[k] = v
|
else
|
||||||
|
clone = {}
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
clone[k] = deep_copy(v, copied)
|
||||||
end
|
end
|
||||||
if ... then
|
setmetatable(clone, getmetatable(t))
|
||||||
return tablex.overlay(a, ...)
|
copied[t] = clone
|
||||||
end
|
end
|
||||||
return a
|
end
|
||||||
|
return clone
|
||||||
|
end
|
||||||
|
-- Recursively copy values of a table.
|
||||||
|
-- Retains the same keys as original table -- they're not cloned.
|
||||||
|
function tablex.deep_copy(t)
|
||||||
|
return deep_copy(t, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Overlay tables directly onto one another, merging them together.
|
||||||
|
-- Doesn't merge tables within.
|
||||||
|
-- Takes as many tables as required,
|
||||||
|
-- overlays them in passed order onto the first,
|
||||||
|
-- and returns the first table.
|
||||||
|
function tablex.shallow_overlay(dest, ...)
|
||||||
|
assert:type(dest, "table", "tablex.shallow_overlay - dest", 1)
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
local t = select(i, ...)
|
||||||
|
assert:type(t, "table", "tablex.shallow_overlay - ...", 1)
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
dest[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Overlay tables directly onto one another, merging them together into something like a union.
|
||||||
|
-- Also overlays nested tables, but doesn't clone them (so a nested table may be added to dest).
|
||||||
|
-- Takes as many tables as required,
|
||||||
|
-- overlays them in passed order onto the first,
|
||||||
|
-- and returns the first table.
|
||||||
|
function tablex.deep_overlay(dest, ...)
|
||||||
|
assert:type(dest, "table", "tablex.deep_overlay - dest", 1)
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
local t = select(i, ...)
|
||||||
|
assert:type(t, "table", "tablex.deep_overlay - ...", 1)
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
if type(v) == "table" and type(dest[k]) == "table" then
|
||||||
|
tablex.deep_overlay(dest[k], v)
|
||||||
|
else
|
||||||
|
dest[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return dest
|
||||||
end
|
end
|
||||||
|
|
||||||
--collapse the first level of a table into a new table of reduced dimensionality
|
--collapse the first level of a table into a new table of reduced dimensionality
|
||||||
@ -417,8 +436,9 @@ function tablex.shallow_equal(a, b)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- second loop to ensure a isn't missing any keys from b.
|
||||||
for k, v in pairs(b) do
|
for k, v in pairs(b) do
|
||||||
if a[k] ~= v then
|
if a[k] == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -442,8 +462,10 @@ function tablex.deep_equal(a, b)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- second loop to ensure a isn't missing any keys from b, so we can skip
|
||||||
|
-- recursion.
|
||||||
for k, v in pairs(b) do
|
for k, v in pairs(b) do
|
||||||
if not tablex.deep_equal(v, a[k]) then
|
if a[k] == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
126
tests.lua
Normal file
126
tests.lua
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
-- Run this file with testy:
|
||||||
|
-- testy.lua tests.lua
|
||||||
|
-- testy sets `...` to "module.test", so ignore that and use module top-level paths.
|
||||||
|
package.path = package.path .. ";../?.lua"
|
||||||
|
|
||||||
|
local assert = require("batteries.assert")
|
||||||
|
local tablex = require("batteries.tablex")
|
||||||
|
|
||||||
|
|
||||||
|
-- tablex {{{
|
||||||
|
|
||||||
|
local function test_shallow_copy()
|
||||||
|
local x,r
|
||||||
|
x = { a = 1, b = 2, c = 3 }
|
||||||
|
r = tablex.shallow_copy(x)
|
||||||
|
assert:equal(r.a, 1)
|
||||||
|
assert:equal(r.b, 2)
|
||||||
|
assert:equal(r.c, 3)
|
||||||
|
|
||||||
|
x = { a = { b = { 2 }, c = { 3 }, } }
|
||||||
|
r = tablex.shallow_copy(x)
|
||||||
|
assert:equal(r.a, x.a)
|
||||||
|
|
||||||
|
x = 10
|
||||||
|
r = tablex.shallow_copy(x)
|
||||||
|
assert:equal(r, x)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function test_deep_copy()
|
||||||
|
local x,r
|
||||||
|
x = { a = 1, b = 2, c = 3 }
|
||||||
|
r = tablex.deep_copy(x)
|
||||||
|
assert:equal(r.a, 1)
|
||||||
|
assert:equal(r.b, 2)
|
||||||
|
assert:equal(r.c, 3)
|
||||||
|
|
||||||
|
x = { a = { b = { 2 }, c = { 3 }, } }
|
||||||
|
r = tablex.deep_copy(x)
|
||||||
|
assert(r.a ~= x.a)
|
||||||
|
assert:equal(r.a.b[1], 2)
|
||||||
|
assert:equal(r.a.c[1], 3)
|
||||||
|
|
||||||
|
x = 10
|
||||||
|
r = tablex.deep_copy(x)
|
||||||
|
assert:equal(r, x)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function test_shallow_overlay()
|
||||||
|
local x,y,r
|
||||||
|
x = { a = 1, b = 2, c = 3 }
|
||||||
|
y = { c = 8, d = 9 }
|
||||||
|
r = tablex.shallow_overlay(x, y)
|
||||||
|
assert(
|
||||||
|
tablex.deep_equal(
|
||||||
|
r,
|
||||||
|
{ a = 1, b = 2, c = 8, d = 9 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
x = { b = { 2 }, c = { 3 }, }
|
||||||
|
y = { c = { 8 }, d = { 9 }, }
|
||||||
|
r = tablex.shallow_overlay(x, y)
|
||||||
|
assert(r.b == x.b)
|
||||||
|
assert(r.c == y.c)
|
||||||
|
assert(r.d == y.d)
|
||||||
|
assert(
|
||||||
|
tablex.deep_equal(
|
||||||
|
r,
|
||||||
|
{ b = { 2 }, c = { 8 }, d = { 9 }, }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function test_deep_overlay()
|
||||||
|
local x,y,r
|
||||||
|
x = { a = 1, b = 2, c = 3 }
|
||||||
|
y = { c = 8, d = 9 }
|
||||||
|
r = tablex.deep_overlay(x, y)
|
||||||
|
assert(
|
||||||
|
tablex.deep_equal(
|
||||||
|
r,
|
||||||
|
{ a = 1, b = 2, c = 8, d = 9 }))
|
||||||
|
|
||||||
|
x = { a = { b = { 2 }, c = { 3 }, } }
|
||||||
|
y = { a = { c = { 8 }, d = { 9 }, } }
|
||||||
|
r = tablex.deep_overlay(x, y)
|
||||||
|
assert(
|
||||||
|
tablex.deep_equal(
|
||||||
|
r,
|
||||||
|
{ a = { b = { 2 }, c = { 8 }, d = { 9 }, } }))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function test_shallow_equal()
|
||||||
|
local x,y
|
||||||
|
x = { a = { b = { 2 }, } }
|
||||||
|
y = { a = { b = { 2 }, } }
|
||||||
|
assert(not tablex.shallow_equal(x, y))
|
||||||
|
|
||||||
|
x = { 3, 4, "hello", [20] = "end", }
|
||||||
|
y = { 3, 4, "hello", [20] = "end", }
|
||||||
|
assert(tablex.shallow_equal(x, y))
|
||||||
|
|
||||||
|
local z = { 1, 2, }
|
||||||
|
x = { a = z, b = 10, c = true, }
|
||||||
|
y = { a = z, b = 10, c = true, }
|
||||||
|
assert(tablex.shallow_equal(x, y))
|
||||||
|
assert(tablex.shallow_equal(y, x))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function test_deep_equal()
|
||||||
|
local x,y
|
||||||
|
x = { a = { b = { 2 }, c = { 3 }, } }
|
||||||
|
y = { a = { b = { 2 }, c = { 3 }, } }
|
||||||
|
assert(tablex.deep_equal(x, y))
|
||||||
|
|
||||||
|
x = { a = { b = { 1, 2 }, c = { 3 }, } }
|
||||||
|
y = { a = { c = { 3 }, b = { [2] = 2, [1] = 1 }, } }
|
||||||
|
assert(tablex.deep_equal(x, y))
|
||||||
|
assert(tablex.deep_equal(y, x))
|
||||||
|
|
||||||
|
x = { a = { b = { 2 }, c = { 3 }, 2 } }
|
||||||
|
y = { a = { b = { 2 }, c = { 3 }, } }
|
||||||
|
assert(not tablex.deep_equal(x, y))
|
||||||
|
assert(not tablex.deep_equal(y, x))
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user