Модуль:Lua-Table
Lua-Table 0.7
Lua tables with superpowers :muscle:
Examples
-- load library
local table = require "Module:Lua-Table"
-- create a normal table
local t = { 1, 2, 2, 3, -4, 5 }
-- remove negative values
t = table.accept(t, table.positive)
-- create an enhanched table from an old one
local tb = table(t)
-- print every element
tb:eachi(print)
-- or print the entire table (with key-value pairs)
print(tb)
-- double and sum values
local sum = tb:map(table.double):reduce(0, table.sum)
-- remove duplicates
tb = table.unique(tb)
-- clear and add values to table
tb:clear()
tb:append(4, 5, 6)
tb:push(1, 2, 3)
-- table equality
local t1 = { 1, 2, 3, x = { 4, 5 } }
local t2 = { 1, 2, 3, 4, 5 }
print(table.equal(t1, t2)) --> false
print(table.equal(t1, t2, true)) --> true
-- operator overloads
local t1 = table { 1, 2, 3 }
local t2 = table { 2, 4, 5 }
print(t1 + t2) -- table.union
print(t1 - t2) -- table.negation
print(t1 * t2) -- table.intersect
print(t1 == t2) -- table.equal
print(t1 .. t2) -- table.merge
Источник
- Лука Анцалоне. Lua-Table 0.7.
-- https://github.com/Luca96/lua-table
-----------------------------------------------------------------------------------------
-- LuaTable: lua tables with superpowers
-----------------------------------------------------------------------------------------
--[[
MIT License
Copyright (c) 2018 Luca Anzalone
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--]]
-----------------------------------------------------------------------------------------
local setmetatable = setmetatable
local assert = assert
local unpack = unpack
local select = select
local error = error
local print = print
local pairs = pairs
local ipairs = ipairs
local type = type
local insert = table.insert
local remove = table.remove
local concat = table.concat
local format = string.format
local random = math.random
local floor = math.floor
local abs = math.abs
local len = string.len
-----------------------------------------------------------------------------------------
-- assertions / warnings
-----------------------------------------------------------------------------------------
local warn = "\27[1;33mWarning at\27[0m"
local term = "\27[1;36m>>>\27[0m"
local err = "\27[1;31mError at\27[0m"
local function assert_init(t)
assert(type(t) == "table",
format("%s table.init(): optional parameter <table> must be a not-nil table!",
err, msg))
end
local function assert_table(msg, t)
assert(type(t) == "table",
format("%s table.%s(): parameter <table> must be a not-nil table!",
err, msg))
end
local function assert_table_func(msg, t, f)
assert(type(t) == "table",
format("%s table.%s(): require a not-nil table!", err, msg))
assert(type(f) == "function",
format("%s table.%s(): require a not-nil function!", err, msg))
end
local function assert_number(fn, num)
assert(type(num) == "number",
format("%s table.%s(): require a number!", err, fn))
end
local function assert_true(f, msg, test)
assert(test, format("%s table.%s(): %s", err, f, msg))
end
local function warn_nil(fn, value)
if value == nil then
print(format("%s %s table.%s(): nil value", term, warn, fn))
end
end
local function warn_if(f, msg, test)
if test then
print(format("%s %s table.%s(): %s", term, warn, f, msg))
end
end
-----------------------------------------------------------------------------------------
-- CLASS
-----------------------------------------------------------------------------------------
local table = table
function table.tostring(t, tab)
-- returns a string representation of the given table
tab = tab or " "
local b = { "table [" }
local i = 2
for k, v in pairs(t) do
if v == nil then
v = ''
elseif type(v) == "table" then
b[i] = format(tab .. "k: %s, v: %s", k, table.tostring(v, tab .. " "))
elseif type(v) ~= "userdata" then
b[i] = format(tab .. "k: %s, v: %s", k, v)
end
i = i + 1
end
b[i] = (tab .. "]"):sub(3)
return concat(b, "\n")
end
function table.info()
-- print library info
print [[
luatable [
version: 0.7,
author: Luca Anzalone,
github: https://github.com/Luca96/lua-table
]
]]
end
local mt = { __index = table, __tostring = table.tostring }
-----------------------------------------------------------------------------------------
-- operators (use these with map, reduce, each, ecc..)
-----------------------------------------------------------------------------------------
function table.void()
end
function table.isnil(a)
return a == nil
end
function table.odd(a)
return a % 2 == 1
end
function table.even(a)
return a % 2 == 0
end
function table.half(a)
return a * .5
end
function table.double(a)
return a * 2
end
function table.sqr(a)
return a * a
end
function table.pow(a, b)
return a ^ b
end
function table.increase(a)
return a + 1
end
function table.decrease(a)
return a - 1
end
function table.positive(a)
return a >= 0
end
function table.negative(a)
return a < 0
end
function table.itself(a)
return a
end
function table.identity(a)
return a == a
end
function table.eq(a, b)
return a == b
end
function table.neq(a, b)
return a ~= b
end
function table.gt(a, b)
return a > b
end
function table.lt(a, b)
return a < b
end
function table.ge(a, b)
return a >= b
end
function table.le(a, b)
return a <= b
end
function table.sum(a, b)
return a + b
end
function table.sub(a, b)
return a - b
end
function table.mul(a, b)
return a * b
end
function table.div(a, b)
return a / b
end
-----------------------------------------------------------------------------------------
-- iterators
-----------------------------------------------------------------------------------------
function table.iter(t)
-- build an iterator through the given table
assert_table("iter", t)
local i = 1
return function()
local v = t[i]
i = i + 1
return v
end
end
function table.inverse(t)
-- build an iterator that iterate in reversed order
assert_table("inverse", t)
local i = #t
return function()
local v = t[i]
i = i - 1
return v
end
end
function table.range(t, start, count, step)
-- build a range iterator
assert_table ("range", t)
step = step or 1
count = count or #t
start = start or 1
assert_number("range", start)
assert_number("range", count)
assert_number("range", step)
local i = start
local c = count
return function()
if c > 0 then
local k = i
local v = t[i]
i = i + step
c = c - 1
return k, v
end
end
end
function table.group(t, k)
-- iterate through table by grouping values together (group size is k)
-- in each iteration, the iterator will return a k-tuple
assert_table("group", t)
assert_true ("group", "k must be > 0!", (type(k) == "number") and (k > 0))
local i = 0
return function()
local values = {}
for j = 1, k do
values[j] = t[i + j]
end
i = i + k
return unpack(values)
end
end
function table.slide(t, k)
-- iterate through table like a sliding-window of size k
-- in each iteration, the iterator will return a k-tuple
assert_table("slide", t)
assert_true ("slide", "k must be > 0!", (type(k) == "number") and (k > 0))
local i = 0
return function()
local values = {}
for j = 1, k do
values[j] = t[i + j]
end
i = i + 1
return unpack(values)
end
end
function table.eachi(t, func, ...)
-- apply the given function to all the elements of the table
assert_table_func("eachi", t, func)
for i = 1, #t do
func(t[i], ...)
end
return t
end
function table.each(t, func, ...)
-- apply the given function on all (key, value) pairs of table
assert_table_func("each", t, func)
for k, v in pairs(t) do
func(k, v, ...)
end
return t
end
function table.keys(t)
-- iterate over all table keys
assert_table("keys", t)
local keys = {}
local j = 1
for k, _ in pairs(t) do
keys[j] = k
j = j + 1
end
j = 1
return function()
local k = keys[j]
j = j + 1
return k
end
end
function table.values(t)
-- iterate over all table values
assert_table("values", t)
local values = {}
local k = 1
for _, v in pairs(t) do
values[k] = v
k = k + 1
end
k = 1
return function()
local v = values[k]
k = k + 1
return v
end
end
-----------------------------------------------------------------------------------------
-- functional utils
-----------------------------------------------------------------------------------------
function table.map(t, func, ...)
-- returns a table which elements are the result of applying the transformation function
assert_table_func("map", t, func)
local map = table()
for i = 1, #t do
map[i] = func(t[i], ...)
end
return map
end
function table.reduce(t, base, reduction, ...)
-- reduce a table into a single value according to the reduction function,
-- base is the initial value of the reduction
assert_table_func("reduce", t, reduction)
local value = base
for i = 1, #t do
value = reduction(value, t[i], ...)
end
return value
end
function table.accept(t, criteria, ...)
-- accept elements that matches the criteria
assert_table_func("accept", t, criteria)
local k = 1
local tb = table()
for i = 1, #t do
local item = t[i]
if criteria(item, ...) then
tb[k] = item
k = k + 1
end
end
return tb
end
function table.reject(t, criteria, ...)
-- remove elements that matches the criteria
assert_table_func("reject", t, criteria)
local k = 1
local tb = table()
for i = 1, #t do
local item = t[i]
if not criteria(item, ...) then
tb[k] = item
k = k + 1
end
end
return tb
end
function table.flat(t)
-- flattens a nested table (over int indices - use table.deepflat instead)
assert_table("flat", t)
local queque = { t }
local result = table()
local base = 1
local top = 1
local k = 1
while base <= top do
local items = queque[base]
base = base + 1
for i = 1, #items do
local v = items[i]
if type(v) == "table" then
top = top + 1
queque[top] = v
else
result[k] = v
k = k + 1
end
end
end
return result
end
function table.deepflat(t)
-- flattens a nested table over all key-value pairs
assert_table("deepflat", t)
local queque = { t }
local result = table()
local base = 1
local top = 1
local k = 1
while base <= top do
local items = queque[base]
base = base + 1
for _, v in pairs(items) do
if type(v) == "table" then
top = top + 1
queque[top] = v
else
result[k] = v
k = k + 1
end
end
end
return result
end
function table.flatmap(t, func, ...)
-- every element returned by the transformation function is flattened and then added
-- to the output table
assert_table_func("flatmap", t, func)
local tb, k = table(), 1
local flat = table.flat
for i = 1, #t do
local items = flat(func(t[i], ...))
for j = 1, #items do
tb[k] = items[j]
k = k + 1
end
end
return tb
end
-----------------------------------------------------------------------------------------
-- utility
-----------------------------------------------------------------------------------------
function table.purify(t)
-- recursively removes all nil values along all key-value pairs
assert_table("purify", t)
local tb = table()
local i = 1
for k, v in pairs(t) do
if type(v) == "table" then
v = table.purify(v)
end
if type(k) == "number" then
tb[i] = v
i = i + 1
else
tb[k] = v
end
end
return tb
end
function table.max(t, comparator)
-- return the biggest value inside the table in base of a comparator function
comparator = comparator or Table.ge
assert_table_func("max", t, comparator)
local max = t[1]
for i = 2, #t do
local item = t[i]
if comparator(item, max) then
max = item
end
end
return max
end
function table.min(t, comparator)
-- return the smallest value inside the table in base of a comparator function
comparator = comparator or Table.ge
assert_table_func("min", t, comparator)
local max = t[1]
for i = 2, #t do
local item = t[i]
if comparator(item, max) then
max = item
end
end
return max
end
function table.avg(t)
-- return the average value inside table
assert_table("avg", t)
local avg = t[1]
local len = #t
if avg == nil then
return 0
end
for i = 2, len do
avg = avg + t[i]
end
return avg / len
end
function table.maximize(t, func, ...)
-- return the value of the table that maximize the function value
assert_table_func("maximize", t, func)
local max_val = t[1]
local max_fun = func(max_val, ...)
for i = 2, #t do
local val = t[i]
local fval = func(val, ...)
if fval > max_fun then
max_fun = fval
max_val = val
end
end
return max_val
end
function table.minimize(t, func, ...)
-- return the value of the table that minimize the function value
assert_table_func("minimize", t, func)
local min_val = t[1]
local min_fun = func(min_val, ...)
for i = 2, #t do
local val = t[i]
local fval = func(val, ...)
if fval < min_fun then
min_fun = fval
min_val = val
end
end
return min_val
end
function table.sample(t)
-- returns a random element of the table
assert_table("sample", t)
local size = #t
if size > 0 then
return t[random(size)]
end
end
function table.shuffle(t)
-- mix the values inside the given table
assert_table("shuffle", t)
local len = #t
for i = 1, len do
local k = random(len)
t[i], t[k] = t[k], t[i]
end
return t
end
function table.reverse(t)
-- return a table which values are in opposite order
assert_table("reverse", t)
local n = #t
local m = floor(n * .5)
for i = 1, m do
local k = n - i + 1
t[i], t[k] = t[k], t[i]
end
return t
end
function table.slice(t, i, j)
-- return a portion of the input table that ranges to i from j
-- nil values are ignored
assert_table("slice", t)
local n = #t
j = j or n
i = i or 1
assert_number("slice", i)
assert_number("slice", j)
if i < 0 then
i = n + 1 + i
if i < 1 then i = 1 end
end
if j < 0 then
j = n + 1 + j
if j < 1 then j = 1 end
end
warn_if("slice", i .. " > " .. j, i > j)
local part = table()
local n = 1
for k = i, j do
local v = t[k]
if v ~= nil then
part[n] = v
n = n + 1
end
end
return part
end
function table.find(t, value)
-- find a value inside the given table, it returns the value's index
-- of the first occurrence if finded otherwise it returns nil
assert_table("find", t)
for i = 1, #t do
if t[i] == value then
return i
end
end
return nil
end
function table.clone(t)
-- clone recursively each key-value pair of the input table
assert_table("clone", t)
local copy = table()
for k, v in pairs(t) do
if type(v) == "table" then
v = table.clone(v)
end
copy[k] = v
end
return copy
end
function table.merge(t1, t2)
-- return a new table which values are taken by both t1 and t2
-- it doesn't remove duplicates (use table.union instead)
assert_table("merge", t1)
assert_table("merge", t2)
local merged = table()
local k = 1
for i = 1, #t1 do
merged[k] = t1[i]
k = k + 1
end
for i = 1, #t2 do
merged[k] = t2[i]
k = k + 1
end
return merged
end
function table.unique(t)
-- remove all duplicates in table (useful to create a table as a set)
assert_table("unique", t)
local set = table()
local tmp = {}
local k = 1
for i = 1, #t do
local v = t[i]
if tmp[v] == nil then
tmp[v] = true
set[k] = v
k = k + 1
end
end
return set
end
function table.keyList(t)
-- return a list of keys of the given table
assert_table("keyList", t)
local list = table()
local i = 1
for k, _ in pairs(t) do
list[i] = k
i = i + 1
end
return list
end
function table.valueList(t)
-- return a list of values of the given table
assert_table("valueList", t)
local list = table()
local k = 1
for _, v in pairs(t) do
list[k] = v
k = k + 1
end
return list
end
function table.valueSequence(t)
-- return a sequence of values, instead of valueList it consider only
-- elements from index 1 to #table (values along keys are ignored)
assert_table("valueSequence", t)
local seq = table()
for i = 1, #t do
seq[i] = t[i]
end
return seq
end
function table.equal(t1, t2, deep)
-- check if t1 contains the same elements contained in t2, if deep is true
-- the equality is spread among all key-value pairs (so not only to int indices)
deep = deep or false
assert_table("equal", t1)
assert_table("equal", t2)
local v1 = deep and table.deepflat(t1) or table.flat(t1)
local v2 = deep and table.deepflat(t2) or table.flat(t2)
local l1 = #v1
local l2 = #v2
if l1 == l2 then
-- compare each values
for i = 1, l1 do
if v1[i] ~= v2[i] then
return false
end
end
return true
end
return false
end
function table.all(t, predicate)
-- returns true if all elements of table t satisfy the given predicate
assert_table_func("all", t, predicate)
for i = 1, #t do
if not predicate(t[i]) then
return false
end
end
return true
end
function table.allPairs(t, predicate)
-- returns true if all key-pair elements of table t satisfy the given predicate
assert_table_func("allPairs", t, predicate)
for _, v in pairs(t) do
if not predicate(v) then
return false
end
end
return true
end
function table.any(t, predicate)
-- returns true if at least an elements of table t satisfy the given predicate
assert_table_func("any", t, predicate)
for i = 1, #t do
if predicate(t[i]) then
return true
end
end
return false
end
function table.anyPairs(t, predicate)
-- returns true if at least a key-pair of table t satisfy the given predicate
assert_table_func("anyPairs", t, predicate)
for _, v in pairs(t) do
if predicate(v) then
return true
end
end
return false
end
-----------------------------------------------------------------------------------------
-- set utility
-----------------------------------------------------------------------------------------
function table.union(t1, t2)
-- return a new table that is the union of t1 and t2
assert_table("union", t1)
assert_table("union", t2)
local tb = table()
local k = 1
for i = 1, #t1 do
tb[k] = t1[i]
k = k + 1
end
for i = 1, #t2 do
tb[k] = t2[i]
k = k + 1
end
return tb:unique()
end
function table.negation(t1, t2)
-- return a new table which values are in t1 but not in t2
assert_table("negation", t1)
assert_table("negation", t2)
local diff = table()
local keys = {}
local size = #t1
local k = 1
for _, v in ipairs(t2) do
keys[v] = true
end
for i = 1, size do
local v = t1[i]
if not keys[v] then
diff[k] = v
k = k + 1
end
end
return diff
end
function table.intersect(t1, t2)
-- return a new table which values are both in t1 and t2
assert_table("intersect", t1)
assert_table("intersect", t2)
local set = {}
local len = #t1
local t = table()
local k = 1
for _, v in ipairs(t2) do
set[v] = true
end
for i = 1, len do
local v = t1[i]
if set[v] then
t[k] = v
k = k + 1
end
end
return t
end
function table.keySet(t)
-- return a set of keys of the given table
assert_table("keySet", t)
local set = table()
for k, _ in pairs(t) do
set[k] = true
end
return set
end
function table.valueSet(t)
-- return a set of values of the given table
assert_table("valueSet", t)
local set = table()
for _, v in pairs(t) do
set[v] = true
end
return set
end
-----------------------------------------------------------------------------------------
-- table operators (no table check on self!)
-----------------------------------------------------------------------------------------
function table:at(index, default)
-- returns the element at the given index, if index is negative it starts
-- counting from the end of the table and then returning the element.
-- 'at' works with integer indices, use get for other key-values (as index).
-- optionally you can specify a default-value that is returned in case
-- table[index] is nil.
assert_number("at", index)
-- positive index
if index >= 0 then
return self[index] or default
end
-- negative index
return self[#self + index + 1] or default
end
function table:get(key, default)
-- returns the element at the given key, if element is nil it returns an
-- optional default value
return self[key] or default
end
function table:append(...)
-- insert one or more elements at the end of the table
local len = #self
local elements = { ... }
for i = 1, #elements do
self[len + i] = elements[i]
end
return self
end
function table:push(...)
-- insert one or more elements at the begin of the table
local elements = { ... }
for i = #elements, 1, -1 do
insert(self, 1, elements[i])
end
return self
end
function table:pop()
-- remove and return the last element into the table
return remove(self, #self)
end
function table:head()
-- remove and return the first element into the table
return remove(self, 1)
end
function table:last()
-- return the last element into the table
return self[#self]
end
function table:first()
-- return the first value into the table
return self[1]
end
function table:clear(remove_pairs)
-- empties the given table, if remove_pairs is true: all key-val pairs will be removed
if remove_pairs == true then
--
for k, v in pairs(self) do
self[k] = nil
end
else
--
for i = 1, #self do
self[i] = nil
end
end
return self
end
function table:has(value)
-- return true if it finds the given value, otherwise returns false
return table.find(self, value) ~= nil
end
function table:haskey(key)
-- returns true if self[key] is not nil
return self[key] ~= nil
end
function table:empty()
-- check if the table has 0 elements, it not consider key pairs.
return #self == 0
end
function table:lshift(pos)
-- left shift the content of the table of 'pos' positions. It returns the
-- shifted elements that are below the first index of the table.
pos = pos or 0
assert_true("lshift", type(pos) == "number" and pos >= 0, "<pos> must be a number >= 0!")
local s, k = {}, 1
for i = 1, pos do
s[k] = remove(self, 1)
k = k + 1
end
return unpack(s)
end
function table:rshift(pos)
-- remove and returns all the elements of the table that are above #self - pos.
pos = pos or 0
assert_true("rshift", type(pos) == "number" and pos >= 0, "<pos> must be a number >= 0!")
local s, k = {}, 0
local size = #self
if size < pos then
pos = size
end
for i = 1, pos do
s[pos - k] = remove(self, size - i + 1)
k = k + 1
end
return unpack(s)
end
function table:shift(pos)
-- calls table.lshift if pos is negative or table.rshift is pos is positive
pos = pos or 0
assert_number("shift", pos)
if pos >= 0 then
return table.rshift(self, pos)
end
return table.lshift(self, pos * -1)
end
-----------------------------------------------------------------------------------------
-- constructors:
-----------------------------------------------------------------------------------------
function table.new(...)
-- creates an enhanched table
local t = { ... }
if #t == 1 and type(t[1]) == "table" then
-- the table is created from an old one
t = t[1]
end
return setmetatable(t, mt)
end
function table.init(size, def, ...)
-- fill the enhanched table with a custom init value or function
assert_number("init", size)
local t = table()
if type(def) == "function" then
--
for i = 1, size do
t[i] = def(i, ...)
end
else
--
for i = 1, size do
t[i] = def
end
end
return t
end
function table.zeros(size)
-- fill the enhanched table with zeros
assert_number("zeros", size)
local t = table()
for i = 1, size do
t[i] = 0
end
return t
end
function table.ones(size)
-- fill the enhanched table with ones
assert_number("ones", size)
local t = table()
for i = 1, size do
t[i] = 1
end
return t
end
function table.ofChars(string)
-- create a enhanched table from the given string, which will contains
-- every single character of the input string
assert_true("ofChars", "require a valid string!", type(string) == "string")
local t = table()
local n = string:len()
for i = 1, n do
t[i] = string:sub(i, i)
end
return t
end
-----------------------------------------------------------------------------------------
-- operator overload
-----------------------------------------------------------------------------------------
mt.__eq = table.equal
mt.__add = table.union
mt.__sub = table.negation
mt.__mul = table.intersect
mt.__concat = table.merge
-----------------------------------------------------------------------------------------
return setmetatable(table, { __call = function(t, ...) return table.new(...) end })
-----------------------------------------------------------------------------------------