2020-01-29 03:26:28 +00:00
|
|
|
--[[
|
|
|
|
extra table routines
|
|
|
|
]]
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--apply prototype to module if it isn't the global table
|
2020-03-15 10:22:22 +00:00
|
|
|
--so it works "as if" it was the global table api
|
|
|
|
--upgraded with these routines
|
2020-04-07 03:49:10 +00:00
|
|
|
|
|
|
|
local tablex = setmetatable({}, {
|
|
|
|
__index = table,
|
|
|
|
})
|
2020-03-15 09:28:50 +00:00
|
|
|
|
|
|
|
--alias
|
2020-04-07 03:49:10 +00:00
|
|
|
tablex.join = tablex.concat
|
2020-03-15 09:28:50 +00:00
|
|
|
|
2020-01-29 03:26:28 +00:00
|
|
|
--return the back element of a table
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.back(t)
|
2020-01-29 03:26:28 +00:00
|
|
|
return t[#t]
|
|
|
|
end
|
|
|
|
|
|
|
|
--remove the back element of a table and return it
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.pop(t)
|
2020-01-29 03:26:28 +00:00
|
|
|
return table.remove(t)
|
|
|
|
end
|
|
|
|
|
|
|
|
--insert to the back of a table
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.push(t, v)
|
2020-01-29 03:26:28 +00:00
|
|
|
return table.insert(t, v)
|
|
|
|
end
|
|
|
|
|
|
|
|
--remove the front element of a table and return it
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.shift(t)
|
2020-01-29 03:26:28 +00:00
|
|
|
return table.remove(t, 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
--insert to the front of a table
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unshift(t, v)
|
2020-01-29 03:26:28 +00:00
|
|
|
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?)
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.index_of(t, a)
|
2020-01-29 03:26:28 +00:00
|
|
|
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
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.remove_value(t, a)
|
|
|
|
local i = tablex.index_of(t, a)
|
2020-01-29 03:26:28 +00:00
|
|
|
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
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.add_value(t, a)
|
|
|
|
local i = tablex.index_of(t, a)
|
2020-01-29 03:26:28 +00:00
|
|
|
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)
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.pick_random(t, r)
|
2020-01-29 03:26:28 +00:00
|
|
|
if #t == 0 then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
return t[_random(1, #t, r)]
|
|
|
|
end
|
|
|
|
|
|
|
|
--shuffle the order of a table
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.shuffle(t, r)
|
2020-01-29 03:26:28 +00:00
|
|
|
for i = 1, #t do
|
|
|
|
local j = _random(1, #t, r)
|
|
|
|
t[i], t[j] = t[j], t[i]
|
|
|
|
end
|
2020-02-05 10:16:23 +00:00
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
--reverse the order of a table
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.reverse(t)
|
2020-02-05 10:16:23 +00:00
|
|
|
for i = 1, #t / 2 do
|
|
|
|
local j = #t - i + 1
|
|
|
|
t[i], t[j] = t[j], t[i]
|
|
|
|
end
|
|
|
|
return t
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--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)
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.keys(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
local r = {}
|
|
|
|
for k,v in pairs(t) do
|
|
|
|
table.insert(r, k)
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
2020-03-15 09:28:50 +00:00
|
|
|
return r
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--collect all values of a keyed table into a sequential table
|
|
|
|
--(shallow copy if it's already sequential)
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.values(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
local r = {}
|
|
|
|
for k,v in pairs(t) do
|
|
|
|
table.insert(r, v)
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
2020-03-15 09:28:50 +00:00
|
|
|
return r
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
2020-03-12 10:15:18 +00:00
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--(might already exist depending on luajit)
|
2020-04-07 03:49:10 +00:00
|
|
|
if tablex.clear == nil then
|
|
|
|
if tablex ~= table and table.clear then
|
2020-03-15 09:28:50 +00:00
|
|
|
--import from global if it exists
|
2020-04-07 03:49:10 +00:00
|
|
|
tablex.clear = table.clear
|
2020-03-15 09:28:50 +00:00
|
|
|
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
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.clear(t)
|
2020-03-28 04:30:25 +00:00
|
|
|
assert(type(t) == "table", "table.clear - argument 't' must be a table")
|
2020-03-15 09:28:50 +00:00
|
|
|
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 :)
|
|
|
|
|
2020-03-12 10:15:18 +00:00
|
|
|
--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)
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.copy(t, deep_or_into)
|
2020-03-12 10:15:18 +00:00
|
|
|
assert(type(t) == "table", "table.copy - argument 't' must be a table")
|
|
|
|
local is_bool = type(deep_or_into) == "boolean"
|
2020-04-07 03:49:10 +00:00
|
|
|
local istablex = type(deep_or_into) == "table"
|
2020-03-12 10:15:18 +00:00
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
local deep = (is_bool and deep_or_into) or istablex
|
|
|
|
local into = istablex and deep_or_into or {}
|
2020-03-12 10:15:18 +00:00
|
|
|
for k,v in pairs(t) do
|
|
|
|
if deep and type(v) == "table" then
|
|
|
|
if type(v.copy) == "function" then
|
|
|
|
v = v:copy()
|
|
|
|
else
|
2020-04-07 03:49:10 +00:00
|
|
|
v = tablex.copy(v, deep)
|
2020-03-12 10:15:18 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
into[k] = v
|
|
|
|
end
|
|
|
|
return into
|
|
|
|
end
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--overlay one table directly onto another, shallow only
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.overlay(to, from)
|
2020-03-15 09:28:50 +00:00
|
|
|
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" :)
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack2(t)
|
2020-03-15 10:22:22 +00:00
|
|
|
return t[1], t[2]
|
2020-03-15 09:28:50 +00:00
|
|
|
end
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack3(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
return t[1], t[2], t[3]
|
|
|
|
end
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack4(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
return t[1], t[2], t[3], t[4]
|
|
|
|
end
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack5(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
return t[1], t[2], t[3], t[4], t[5]
|
|
|
|
end
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack6(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
return t[1], t[2], t[3], t[4], t[5], t[6]
|
|
|
|
end
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack7(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
return t[1], t[2], t[3], t[4], t[5], t[6], t[7]
|
|
|
|
end
|
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
function tablex.unpack8(t)
|
2020-03-15 09:28:50 +00:00
|
|
|
return t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8]
|
|
|
|
end
|
2020-04-07 03:49:10 +00:00
|
|
|
|
|
|
|
return tablex
|