508 lines
12 KiB
Lua
508 lines
12 KiB
Lua
-- Copies the table deeply, including metatables
|
|
function table.copy(t)
|
|
local t_type = type(t)
|
|
local copy
|
|
if t_type == "table" then
|
|
copy = {}
|
|
for k, v in next, t, nil do
|
|
copy[table.copy(k)] = table.copy(v)
|
|
end
|
|
setmetatable(copy, table.copy(getmetatable(t)))
|
|
else
|
|
copy = t
|
|
end
|
|
return copy
|
|
end
|
|
|
|
|
|
-- Copies the table shallowly, meaning that tables inside the table will not be created anew, and only be referenced in the copy
|
|
function table.shallow_copy(t)
|
|
local copy = {}
|
|
for k, v in pairs(t) do
|
|
copy[k] = v
|
|
end
|
|
return copy
|
|
end
|
|
|
|
|
|
-- t = {}
|
|
-- table.array(t, 5) -> {1, 2, 3, 4, 5}
|
|
function table.array(t, n)
|
|
for i = 1, n do
|
|
table.push(t, i)
|
|
end
|
|
return t
|
|
end
|
|
|
|
-- t = {"a", "b", "c", "d"}
|
|
-- table.get(t, 1) -> "a"
|
|
-- table.get(t, 1, 3) -> {"a", "b", "c"}
|
|
-- table.get(t, 2, -1) -> {"b", "c", "d"}
|
|
function table.get(t, i, j)
|
|
if i < 0 then i = #t + i + 1 end
|
|
if not j then return t[i] end
|
|
if j < 0 then j = #t + j + 1 end
|
|
if i == j then return t[i] end
|
|
local out = {}
|
|
for k = i, j, math.sign(j-i) do
|
|
table.push(out, t[k])
|
|
end
|
|
return out
|
|
end
|
|
|
|
|
|
-- t = {"a", "b", "c", "d"}
|
|
-- table.set(t, 1, 1) -> {1, "b", "c", "d"}
|
|
-- table.set(t, 1, 3, 2) -> {2, 2, 2, "d"}
|
|
-- table.set(t, 2, -1, 3) -> {"a", 3, 3, 3}
|
|
function table.set(t, i, j, v)
|
|
if i < 0 then i = #t + i + 1 end
|
|
if not v then t[i] = j; return t end
|
|
if j < 0 then j = #t + j + 1 end
|
|
if i == j then t[i] = v; return t end
|
|
for k = i, j, math.sign(j-i) do
|
|
t[k] = v
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Returns the index of the first instance of value v
|
|
-- t = {4, 3, 2, 4, "a", 1, "a"}
|
|
-- table.index(t, 4) -> 1
|
|
-- table.index(t, "a") -> 5
|
|
function table.index(t, v)
|
|
for i, u in ipairs(t) do
|
|
if u == v then
|
|
return i
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Returns the last value of the table
|
|
-- t = {1, 2, 3, 4}
|
|
-- table.back(t) -> 4
|
|
function table.back(t)
|
|
return t[#t]
|
|
end
|
|
|
|
|
|
-- Returns the first n values
|
|
-- t = {4, 3, 2, 1}
|
|
-- table.head(t) -> 4
|
|
-- table.head(t, 2) -> {4, 3}
|
|
-- If n is defined then it always returns a table, even with only 1 value.
|
|
function table.head(t, n)
|
|
local out = {}
|
|
for i = 1, (n or 1) do
|
|
table.push(out, t[i])
|
|
end
|
|
if n then return out
|
|
else
|
|
if #out == 1 then return out[1]
|
|
else return out end
|
|
end
|
|
end
|
|
|
|
|
|
-- Returns the last n values
|
|
-- t = {5, 4, 3, 2, 1}
|
|
-- table.tail(t) -> 1
|
|
-- table.tail(t, 2) -> {2, 1}
|
|
-- If n is defined then it always returns a table, even with only 1 value.
|
|
function table.tail(t, n)
|
|
local out = {}
|
|
for i = #t-(n or #t-1)+1, #t do
|
|
table.push(out, t[i])
|
|
end
|
|
if n then return out
|
|
else
|
|
if #out == 1 then return out[1]
|
|
else return out end
|
|
end
|
|
end
|
|
|
|
|
|
-- Inserts value at the end of the table
|
|
-- t = {1, 2}
|
|
-- table.push(t, "a") -> {1, 2, "a"}
|
|
function table.push(t, v)
|
|
table.insert(t, v)
|
|
return t
|
|
end
|
|
|
|
|
|
-- Removes the first n values and returns them as well as the modified table
|
|
-- t = {4, 3, 2, 1}
|
|
-- table.shift(t) -> 4, {3, 2, 1}
|
|
-- table.shift(t, 3) -> {4, 3, 2}, {1}
|
|
function table.shift(t, n)
|
|
local out = {}
|
|
for i = 1, (n or 1) do
|
|
table.insert(out, table.remove(t, 1))
|
|
end
|
|
if #out == 1 then return out[1], t
|
|
else return out, t end
|
|
end
|
|
|
|
|
|
-- Inserts values at the start of the table
|
|
-- t = {3, 4}
|
|
-- table.unshift(t, 1, 2) -> {1, 2, 3, 4}
|
|
function table.unshift(t, ...)
|
|
for j, v in ipairs({...}) do
|
|
table.insert(t, 1+j-1, v)
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Removes the last value and returns it as well as the modified table
|
|
-- t = {1, 2, 3, 4}
|
|
-- table.pop(t) -> 4, {1, 2, 3}
|
|
function table.pop(t)
|
|
return table.remove(t, #t), t
|
|
end
|
|
|
|
|
|
-- Deletes all instances of value v
|
|
-- t = {1, 1, 2, 3, 2, 3, 4, 4}
|
|
-- table.delete(t, 1) -> {2, 3, 2, 3, 4, 4}
|
|
-- t = {{id = 1}, {id = 1}, {id = 2}}
|
|
-- table.delete(t, function(v) return v.id == 1 end) -> {{id = 2}}
|
|
function table.delete(t, v)
|
|
if type(v) == 'function' then
|
|
for i = #t, 1, -1 do
|
|
if v(t[i]) then
|
|
table.remove(t, i)
|
|
end
|
|
end
|
|
else
|
|
for i = #t, 1, -1 do
|
|
if v == t[i] then
|
|
table.remove(t, i)
|
|
end
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Deletes the elements in the given range and returns them as well as the modified table
|
|
-- t = {1, 2, 3}
|
|
-- table.slice(t, 1) -> 1, {2, 3}
|
|
-- table.slice(t, 2, -1) -> {2, 3}, 1
|
|
function table.slice(t, i, j)
|
|
if i < 0 then i = #t + i + 1 end
|
|
if not j then return t[i] end
|
|
if j < 0 then j = #t + j + 1 end
|
|
if i == j then return t[i] end
|
|
local out = {}
|
|
for k = j, i, -math.sign(j-i) do
|
|
table.insert(out, table.remove(t, k))
|
|
end
|
|
if #out == 1 then return out[1], t
|
|
else return table.reverse(out), t end
|
|
end
|
|
|
|
|
|
-- Removes duplicates
|
|
-- t = {1, 1, 2, 2, 3, 3}
|
|
-- table.unify(t) -> {1, 2, 3}
|
|
-- t = {{id = 1}, {id = 1}, {id = 2}}
|
|
-- table.unify(t, function(v) return v.id end) -> {{id = 1}, {id = 2}}
|
|
function table.unify(t, f)
|
|
if not f then f = function(v) return v end end
|
|
local seen = {}
|
|
for i = #t, 1, -1 do
|
|
if not seen[f(t[i])] then
|
|
seen[f(t[i])] = true
|
|
else
|
|
table.remove(t, i)
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Counts the number of elements in the table
|
|
-- t = {1, 1, 5, 5, 4, 1, 3, 2, 0, 9, 8, 5, 1, 5, 5, 4, 6}
|
|
-- table.count(t, 1) -> 4
|
|
-- table.count(t, 5) -> 5
|
|
-- table.count(t, 6) -> 1
|
|
-- table.count(t, 4) -> 2
|
|
function table.count(t, v)
|
|
local n = 0
|
|
for i = 1, #t do
|
|
if t[i] == v then
|
|
n = n + 1
|
|
end
|
|
end
|
|
return n
|
|
end
|
|
|
|
|
|
-- Applies function f to all table elements and replaces each element for the value returned by f
|
|
function table.map(t, f, ...)
|
|
for k, v in ipairs(t) do
|
|
t[k] = f(v, k, ...)
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Applies function f to all table elements resulting in a single output value
|
|
-- t = {1, 2, 3}
|
|
-- table.reduce(t, function(memo, v) return memo + v end) -> 6
|
|
-- t = {'a', 'b', 'c'}
|
|
-- table.reduce(t, function(memo, v) return memo .. v end) -> 'abc'
|
|
-- t = {{id = 1}, {id = 2}, {id = 3}}
|
|
-- table.reduce(t, function(memo, v) return memo + v.id end, 0) -> 6
|
|
-- The memo variable starts as the first argument in the array, but sometimes, as in the last example, that's not the desired functionality.
|
|
-- For those cases the third argument comes in handy and can be used to set the initial value of memo directly.
|
|
function table.reduce(t, f, dv, ...)
|
|
local memo = dv or t[1]
|
|
if dv then
|
|
for i = 1, #t do
|
|
memo = f(memo, t[i], i, ...)
|
|
end
|
|
else
|
|
for i = 2, #t do
|
|
memo = f(memo, t[i], i, ...)
|
|
end
|
|
end
|
|
return memo
|
|
end
|
|
|
|
|
|
-- Applies function f to all array elements without changing the array
|
|
function table.foreach(t, f, ...)
|
|
for k, v in ipairs(t) do
|
|
f(v, k, ...)
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Applies function f to all array elements and adds the results to a new array
|
|
function table.foreachn(t, f, ...)
|
|
local out = {}
|
|
for k, v in ipairs(t) do
|
|
table.insert(out, f(v, k, ...))
|
|
end
|
|
return out
|
|
end
|
|
|
|
|
|
-- Applies filter function f which removes all elements that pass the filter and returns them as well as the modified table
|
|
-- t = {1, 2, 3, 4}
|
|
-- table.reject(t, function(v) return v >= 3 end) -> {3, 4}, {1, 2}
|
|
-- table.reject(t, function(v) return v == 1 end) -> 1, {2, 3, 4}
|
|
function table.reject(t, f, ...)
|
|
local out = {}
|
|
for i = #t, 1, -1 do
|
|
if f(t[i], i, ...) then
|
|
table.insert(out, table.remove(t, i))
|
|
end
|
|
end
|
|
if #out == 1 then return out[1], t
|
|
else return table.reverse(out), t end
|
|
end
|
|
|
|
|
|
-- Applies filter function f which collects all elements that pass the filter and returns them; the original table is not modified
|
|
-- t = {1, 2, 3, 4}
|
|
-- table.select(t, function(v) return v >= 3 end) -> {3, 4}
|
|
-- table.select(t, function(v) return v == 1 end) -> 1
|
|
function table.select(t, f, ...)
|
|
local out = {}
|
|
for i = 1, #t do
|
|
if f(t[i], i, ...) then
|
|
table.insert(out, t[i])
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
|
|
-- Returns true if any values in the table pass the test
|
|
function table.any(t, f, ...)
|
|
for i, v in ipairs(t) do
|
|
if f(v, i, ...) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Returns true if all values in the table pass the test
|
|
function table.all(t, f, ...)
|
|
for i, v in ipairs(t) do
|
|
if not f(v, i, ...) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
-- Check if table contains value v and return its index
|
|
-- If value v is a function instead then it checks according to the check performed by that function
|
|
-- t = {4, 3, 2, 1}
|
|
-- table.contains(t, 4) -> 1
|
|
-- t = {{id = 4}, {id = 3}, {id = 2}, {id = 1}}
|
|
-- table.contains(t, function(v) return v.id == 3 end) -> 2
|
|
function table.contains(t, v)
|
|
if type(v) == "function" then
|
|
for i, u in ipairs(t) do
|
|
if v(u) then return i end
|
|
end
|
|
else
|
|
for i, u in ipairs(t) do
|
|
if u == v then return i end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- t = {{1, 2}, {3, {4, 5}}, {6, 7}, 8}
|
|
-- table.flatten(t) -> {1, 2, 3, 4, 5, 6, 7, 8}
|
|
-- table.flatten(t, true) -> {1, 2, 3, {4, 5}, 6, 7, 8}
|
|
function table.flatten(t, shallow)
|
|
local out = {}
|
|
local u
|
|
for k, v in ipairs(t) do
|
|
if type(v) == "table" and getmetatable(t) == nil then
|
|
u = shallow and v or table.flatten(v)
|
|
for _, x in ipairs(u) do
|
|
table.insert(out, x)
|
|
end
|
|
else
|
|
table.insert(out, v)
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
|
|
-- t = {1, 2, 3, 4}
|
|
-- table.tostring(t) -> '{[1] = 1, [2] = 2, [3] = 3, [4] = 4}'
|
|
function table.tostring(t)
|
|
if type(t) == "table" then
|
|
local str = "{"
|
|
for k, v in pairs(t) do
|
|
if type(k) ~= "number" then k = '"' .. k .. '"' end
|
|
str = str .. "[" .. k .. "] = " .. table.tostring(v) .. ", "
|
|
end
|
|
if str ~= "{" then return str:sub(1, -3) .. "}"
|
|
else return str .. "}" end
|
|
elseif type(t) == "string" then
|
|
return '"' .. tostring(t) .. '"'
|
|
else return tostring(t) end
|
|
end
|
|
|
|
|
|
-- Returns the first n values, same as head
|
|
-- t = {4, 3, 2, 1}
|
|
-- table.first(t) -> 4
|
|
-- table.first(t, 2) -> {4, 3}
|
|
function table.first(t, n)
|
|
if n == 1 then return t[1] end
|
|
local out = {}
|
|
for i = 1, (n or 1) do
|
|
table.push(out, t[i])
|
|
end
|
|
if #out == 1 then return out[1]
|
|
else return out end
|
|
end
|
|
|
|
|
|
function table.first2(t, n)
|
|
if n == 1 then return {t[1]} end
|
|
local out = {}
|
|
for i = 1, (n or 1) do
|
|
table.push(out, t[i])
|
|
end
|
|
return out
|
|
end
|
|
|
|
|
|
-- Returns the last n values, same as tail
|
|
-- t = {4, 3, 2, 1}
|
|
-- table.last(t) -> 1
|
|
-- table.last(t, 2) -> {2, 1}
|
|
function table.last(t, n)
|
|
if n == 1 then return t[#t] end
|
|
local out = {}
|
|
for i = #t-n+1, #t do
|
|
table.push(out, t[i])
|
|
end
|
|
if #out == 1 then return out[1]
|
|
else return out end
|
|
end
|
|
|
|
|
|
-- t = {"a", "b", "c", "d"}
|
|
-- table.reverse(t) -> {"d", "c", "b", "a"}
|
|
-- table.reverse(t, 2, 3) -> {"a", "c", "b", "d"}
|
|
-- table.reverse(t, 2, -1) -> {"a", "d", "c", "b"}
|
|
function table.reverse(t, i, j)
|
|
if not i then i = 1 end
|
|
if i < 0 then i = #t + i + 1 end
|
|
if not j then j = #t end
|
|
if j < 0 then j = #t + j + 1 end
|
|
if i == j then return t end
|
|
for k = 0, (j-i+1)/2-1, math.sign(j-i) do
|
|
t[i+k], t[j-k] = t[j-k], t[i+k]
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Shifts the table to the right n times, the last value is warped over to become the first value
|
|
-- t = {1, 2, 3, 4}
|
|
-- table.rotate(t) -> {4, 1, 2, 3}
|
|
-- table.rotate(t, 2) -> {3, 4, 1, 2}
|
|
function table.rotate(t, n)
|
|
if not n then n = 1 end
|
|
if n < 0 then n = #t + n end
|
|
t = table.reverse(t, 1, #t)
|
|
t = table.reverse(t, 1, #t-n)
|
|
t = table.reverse(t, #t-n+1, #t)
|
|
return t
|
|
end
|
|
|
|
|
|
-- Returns a random value from the table
|
|
-- t = {1, 2, 3}
|
|
-- table.random(t) -> 1 or 2 or 3 randomly
|
|
function table.random(t)
|
|
return t[love.math.random(1, #t)]
|
|
end
|
|
|
|
|
|
-- t = {1, 2, 3, 4, 5}
|
|
-- table.shuffle(t) -> {3, 4, 1, 2, 5}
|
|
-- table.shuttle(t) -> {2, 5, 1, 4, 3}
|
|
-- table.shuffle(t) -> {5, 4, 1, 3, 2}
|
|
function table.shuffle(t)
|
|
for i = #t, 2, -1 do
|
|
local j = love.math.random(i)
|
|
t[i], t[j] = t[j], t[i]
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
-- Merges both tables based on their indexes, if the second table has values in the same indexes as the first table then those will overwrite the first values.
|
|
-- t1 = {1, 2, ['a'] = 3, ['b'] = function() end}
|
|
-- t2 = {nil, 8, 4, 5, ['a'] = 8}
|
|
-- table.merge(t1, t2) -> {1, 2, 8, 4, 5, ['a'] = 8, ['b'] = function() end}
|
|
function table.merge(t1, t2)
|
|
local out = {}
|
|
for k, v in pairs(t1) do out[k] = v end
|
|
for k, v in pairs(t2) do out[k] = v end
|
|
return out
|
|
end
|