batteries/table.lua
2020-03-15 21:22:22 +11:00

240 lines
5.5 KiB
Lua

--[[
extra table routines
optional:
set BATTERIES_TABLE_MODULE to a table before requiring
if you don't want this to modify the global `table` table
]]
local _table = BATTERIES_TABLE_MODULE or table
--apply prototype to module if it isn't the global table
--so it works "as if" it was the global table api
--upgraded with these routines
if _table ~= table then
setmetatable(_table, {
__index = table,
})
end
--alias
_table.join = _table.concat
--return the back element of a table
function _table.back(t)
return t[#t]
end
--remove the back element of a table and return it
function _table.pop(t)
return table.remove(t)
end
--insert to the back of a table
function _table.push(t, v)
return table.insert(t, v)
end
--remove the front element of a table and return it
function _table.shift(t)
return table.remove(t, 1)
end
--insert to the front of a table
function _table.unshift(t, v)
return table.insert(t, 1, v)
end
--find the index in a sequential table that a resides at
--or nil if nothing was found
--(todo: consider pairs version?)
function _table.index_of(t, a)
if a == nil then return nil end
for i,b in ipairs(t) do
if a == b then
return i
end
end
return nil
end
--remove the first instance of value from a table (linear search)
--returns true if the value was removed, else false
function _table.remove_value(t, a)
local i = _table.index_of(t, a)
if i then
table.remove(t, i)
return true
end
return false
end
--add a value to a table if it doesn't already exist (linear search)
--returns true if the value was added, else false
function _table.add_value(t, a)
local i = _table.index_of(t, a)
if not i then
table.insert(t, a)
return true
end
return false
end
--helper for optionally passed random
local _global_random = love.math.random or math.random
local function _random(min, max, r)
return r and r:random(min, max)
or _global_random(min, max)
end
--pick a random value from a table (or nil if it's empty)
function _table.pick_random(t, r)
if #t == 0 then
return nil
end
return t[_random(1, #t, r)]
end
--shuffle the order of a table
function _table.shuffle(t, r)
for i = 1, #t do
local j = _random(1, #t, r)
t[i], t[j] = t[j], t[i]
end
return t
end
--reverse the order of a table
function _table.reverse(t)
for i = 1, #t / 2 do
local j = #t - i + 1
t[i], t[j] = t[j], t[i]
end
return t
end
--collect all keys of a table into a sequential table
--(useful if you need to iterate non-changing keys often and want an nyi tradeoff;
-- this call will be slow but then following iterations can use ipairs)
function _table.keys(t)
local r = {}
for k,v in pairs(t) do
table.insert(r, k)
end
return r
end
--collect all values of a keyed table into a sequential table
--(shallow copy if it's already sequential)
function _table.values(t)
local r = {}
for k,v in pairs(t) do
table.insert(r, v)
end
return r
end
--(might already exist depending on luajit)
if _table.clear == nil then
if _table ~= table and table.clear then
--import from global if it exists
_table.clear = table.clear
else
--remove all values from a table
--useful when multiple references are floating around
--so you cannot just pop a new table out of nowhere
function _table.clear(t)
assert(type(to) == "table", "table.clear - argument 't' must be a table")
local k = next(t)
while k ~= nil do
t[k] = nil
k = next(t)
end
end
end
end
--note:
-- copies and overlays are currently not satisfactory
--
-- i feel that copy especially tries to do too much and
-- probably they should be split into separate functions
-- 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 _table.copy(t, deep_or_into)
assert(type(t) == "table", "table.copy - argument 't' must be a table")
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
if deep and type(v) == "table" then
if type(v.copy) == "function" then
v = v:copy()
else
v = _table.copy(v, deep)
end
end
into[k] = v
end
return into
end
--overlay one table directly onto another, shallow only
function _table.overlay(to, from)
assert(type(to) == "table", "table.overlay - argument 'to' must be a table")
assert(type(from) == "table", "table.overlay - argument 'from' must be a table")
for k,v in pairs(from) do
to[k] = v
end
return to
end
--faster unpacking for known-length tables up to 8
--gets around nyi in luajit
--note: you can use a larger unpack than you need as the rest
-- can be discarded, but it "feels dirty" :)
function _table.unpack2(t)
return t[1], t[2]
end
function _table.unpack3(t)
return t[1], t[2], t[3]
end
function _table.unpack4(t)
return t[1], t[2], t[3], t[4]
end
function _table.unpack5(t)
return t[1], t[2], t[3], t[4], t[5]
end
function _table.unpack6(t)
return t[1], t[2], t[3], t[4], t[5], t[6]
end
function _table.unpack7(t)
return t[1], t[2], t[3], t[4], t[5], t[6], t[7]
end
function _table.unpack8(t)
return t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8]
end