603 lines
11 KiB
Lua
603 lines
11 KiB
Lua
--
|
|
-- table.lua
|
|
-- Additions to Lua's built-in table functions.
|
|
-- Copyright (c) 2002-2013 Jason Perkins and the Premake project
|
|
--
|
|
|
|
|
|
--
|
|
-- Make a copy of the indexed elements of the table.
|
|
--
|
|
|
|
function table.arraycopy(object)
|
|
local result = {}
|
|
for i, value in ipairs(object) do
|
|
result[i] = value
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Returns true if the table contains the specified value.
|
|
--
|
|
|
|
function table.contains(t, value)
|
|
for _,v in pairs(t) do
|
|
if (v == value) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
--
|
|
-- Make a shallow copy of a table
|
|
--
|
|
|
|
function table.shallowcopy(object)
|
|
local copy = {}
|
|
for k, v in pairs(object) do
|
|
copy[k] = v
|
|
end
|
|
return copy
|
|
end
|
|
|
|
|
|
--
|
|
-- Make a complete copy of a table, including any child tables it contains.
|
|
--
|
|
|
|
function table.deepcopy(object)
|
|
-- keep track of already seen objects to avoid loops
|
|
local seen = {}
|
|
|
|
local function copy(object)
|
|
if type(object) ~= "table" then
|
|
return object
|
|
elseif seen[object] then
|
|
return seen[object]
|
|
end
|
|
|
|
local clone = {}
|
|
seen[object] = clone
|
|
for key, value in pairs(object) do
|
|
clone[key] = copy(value)
|
|
end
|
|
|
|
setmetatable(clone, getmetatable(object))
|
|
return clone
|
|
end
|
|
|
|
return copy(object)
|
|
end
|
|
|
|
|
|
--
|
|
-- Enumerates an array of objects and returns a new table containing
|
|
-- only the value of one particular field.
|
|
--
|
|
|
|
function table.extract(arr, fname)
|
|
local result = { }
|
|
for _,v in ipairs(arr) do
|
|
table.insert(result, v[fname])
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Enumerates an array of objects and returns a new table containing
|
|
-- only the values satisfying the given predicate.
|
|
--
|
|
|
|
function table.filter(arr, fn)
|
|
local result = { }
|
|
table.foreachi(arr, function(val)
|
|
if fn(val) then
|
|
table.insert(result, val)
|
|
end
|
|
end)
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Flattens a hierarchy of tables into a single array containing all
|
|
-- of the values.
|
|
--
|
|
|
|
function table.flatten(arr)
|
|
local result = {}
|
|
|
|
local function flatten(arr)
|
|
local n = #arr
|
|
for i = 1, n do
|
|
local v = arr[i]
|
|
if type(v) == "table" then
|
|
flatten(v)
|
|
elseif v then
|
|
table.insert(result, v)
|
|
end
|
|
end
|
|
end
|
|
|
|
flatten(arr)
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Walk the elements of an array and call the specified function
|
|
-- for each non-nil element. This works around a "feature" of the
|
|
-- ipairs() function that stops iteration at the first nil.
|
|
--
|
|
-- @param arr
|
|
-- The array to iterate.
|
|
-- @param func
|
|
-- The function to call. The value (not the index) will be passed
|
|
-- as the only argument.
|
|
--
|
|
|
|
function table.foreachi(arr, func)
|
|
if arr then
|
|
local n = #arr
|
|
for i = 1, n do
|
|
local v = arr[i]
|
|
if v then
|
|
func(v, i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Merge two lists into an array of objects, containing pairs
|
|
-- of values, one from each list.
|
|
--
|
|
|
|
function table.fold(list1, list2)
|
|
local result = {}
|
|
for _, item1 in ipairs(list1 or {}) do
|
|
if list2 and #list2 > 0 then
|
|
for _, item2 in ipairs(list2) do
|
|
table.insert(result, { item1, item2 })
|
|
end
|
|
else
|
|
table.insert(result, { item1 })
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Merges an array of items into a string.
|
|
--
|
|
|
|
function table.implode(arr, before, after, between)
|
|
local result = ""
|
|
for _,v in ipairs(arr) do
|
|
if (result ~= "" and between) then
|
|
result = result .. between
|
|
end
|
|
result = result .. before .. v .. after
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
|
|
--
|
|
-- Looks for an object within an array. Returns its index if found,
|
|
-- or nil if the object could not be found.
|
|
--
|
|
|
|
function table.indexof(tbl, obj)
|
|
for k, v in ipairs(tbl) do
|
|
if v == obj then
|
|
return k
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Looks for an object within a table. Returns the key if found,
|
|
-- or nil if the object could not be found.
|
|
--
|
|
|
|
function table.findKeyByValue(tbl, obj)
|
|
for k, v in pairs(tbl) do
|
|
if v == obj then
|
|
return k
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
---
|
|
-- Insert a new value into a table in the position after the specified
|
|
-- existing value. If the specified value does not exist in the table,
|
|
-- the new value is appended to the end of the table.
|
|
--
|
|
-- @param tbl
|
|
-- The table in which to insert.
|
|
-- @param after
|
|
-- The existing value to insert after.
|
|
-- @param value
|
|
-- The new value to insert.
|
|
--
|
|
|
|
function table.insertafter(tbl, after, value)
|
|
local i = table.indexof(tbl, after)
|
|
if i then
|
|
table.insert(tbl, i + 1, value)
|
|
else
|
|
table.insert(tbl, value)
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Inserts a value or array of values into a table. If the value is
|
|
-- itself a table, its contents are enumerated and added instead. So
|
|
-- these inputs give these outputs:
|
|
--
|
|
-- "x" -> { "x" }
|
|
-- { "x", "y" } -> { "x", "y" }
|
|
-- { "x", { "y" }} -> { "x", "y" }
|
|
--
|
|
|
|
function table.insertflat(tbl, values)
|
|
if values == nil then
|
|
return
|
|
elseif type(values) == "table" then
|
|
for _, value in ipairs(values) do
|
|
table.insertflat(tbl, value)
|
|
end
|
|
else
|
|
table.insert(tbl, values)
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
|
|
--
|
|
-- Inserts a value into a table as both a list item and a key-value pair.
|
|
-- Useful for set operations. Returns false if the value already exists, true otherwise.
|
|
--
|
|
|
|
function table.insertkeyed(tbl, pos, value)
|
|
if value == nil then
|
|
value = pos
|
|
pos = #tbl + 1
|
|
end
|
|
|
|
if tbl[value] ~= nil then
|
|
return false
|
|
end
|
|
|
|
table.insert(tbl, pos, value)
|
|
tbl[value] = value
|
|
return true
|
|
end
|
|
|
|
|
|
--
|
|
-- Inserts a value into a table in sorted order. Assumes that the
|
|
-- table is already sorted according to the sort function. If fn is
|
|
-- nil, the table is sorted according to the < operator.
|
|
--
|
|
|
|
function table.insertsorted(tbl, value, fn)
|
|
if value == nil then
|
|
return
|
|
else
|
|
fn = fn or function(a, b) return a < b end
|
|
|
|
local minindex = 1
|
|
local maxindex = #tbl + 1
|
|
while minindex < maxindex do
|
|
local index = minindex + ((maxindex - minindex) >> 1)
|
|
local test = tbl[index]
|
|
if fn(value, test) then
|
|
maxindex = index
|
|
else
|
|
minindex = index + 1
|
|
if not fn(test, value) then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
table.insert(tbl, minindex, value)
|
|
end
|
|
|
|
return tbl
|
|
end
|
|
|
|
|
|
--
|
|
-- Returns true if the table is empty, and contains no indexed or keyed values.
|
|
--
|
|
|
|
function table.isempty(t)
|
|
return next(t) == nil
|
|
end
|
|
|
|
|
|
--
|
|
-- Adds the values from one array to the end of another and
|
|
-- returns the result.
|
|
--
|
|
|
|
function table.join(...)
|
|
local result = { }
|
|
local arg = {...}
|
|
for _,t in ipairs(arg) do
|
|
if type(t) == "table" then
|
|
for _,v in ipairs(t) do
|
|
table.insert(result, v)
|
|
end
|
|
else
|
|
table.insert(result, t)
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Return a list of all keys used in a table.
|
|
--
|
|
|
|
function table.keys(tbl)
|
|
local keys = {}
|
|
for k, _ in pairs(tbl) do
|
|
table.insert(keys, k)
|
|
end
|
|
return keys
|
|
end
|
|
|
|
|
|
--
|
|
-- Adds the key-value associations from one table into another
|
|
-- and returns the resulting merged table.
|
|
--
|
|
|
|
function table.merge(...)
|
|
local result = {}
|
|
local arg = {...}
|
|
for _,t in ipairs(arg) do
|
|
|
|
if type(t) == "table" then
|
|
for k,v in pairs(t) do
|
|
if type(result[k]) == "table" and type(v) == "table" then
|
|
result[k] = table.merge(result[k], v)
|
|
else
|
|
result[k] = v
|
|
end
|
|
end
|
|
|
|
else
|
|
error("invalid value")
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
|
|
---
|
|
-- Replace all instances of `value` with `replacement` in an array. Array
|
|
-- elements are modified in place.
|
|
--
|
|
-- @param value
|
|
-- The value to be replaced.
|
|
-- @param replacement
|
|
-- The new value.
|
|
---
|
|
|
|
function table.replace(self, value, replacement)
|
|
for i = 1, #self do
|
|
if self[i] == value then
|
|
self[i] = replacement
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Translates the values contained in array, using the specified
|
|
-- translation table, and returns the results in a new array.
|
|
--
|
|
|
|
function table.translate(arr, translation)
|
|
if not translation then return {} end
|
|
|
|
local result = {}
|
|
for i = 1, #arr do
|
|
local tvalue
|
|
if type(translation) == "function" then
|
|
tvalue = translation(arr[i])
|
|
else
|
|
tvalue = translation[arr[i]]
|
|
end
|
|
if (tvalue) then
|
|
table.insert(result, tvalue)
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
--
|
|
-- Dumps a table to a string
|
|
--
|
|
function table.tostring(tab, recurse, indent)
|
|
local res = ''
|
|
|
|
if not indent then
|
|
indent = 0
|
|
end
|
|
|
|
local format_value = function(k, v, i)
|
|
formatting = string.rep("\t", i)
|
|
|
|
if k then
|
|
if type(k) == "table" then
|
|
k = '[table]'
|
|
end
|
|
formatting = formatting .. k .. ": "
|
|
end
|
|
|
|
if not v then
|
|
return formatting .. '(nil)'
|
|
elseif type(v) == "table" then
|
|
if recurse and recurse > 0 then
|
|
return formatting .. '\n' .. table.tostring(v, recurse-1, i+1)
|
|
else
|
|
return formatting .. "<table>"
|
|
end
|
|
elseif type(v) == "function" then
|
|
return formatting .. tostring(v)
|
|
elseif type(v) == "userdata" then
|
|
return formatting .. "<userdata>"
|
|
elseif type(v) == "boolean" then
|
|
if v then
|
|
return formatting .. 'true'
|
|
else
|
|
return formatting .. 'false'
|
|
end
|
|
else
|
|
return formatting .. v
|
|
end
|
|
end
|
|
|
|
if type(tab) == "table" then
|
|
local first = true
|
|
|
|
-- add the meta table.
|
|
local mt = getmetatable(tab)
|
|
if mt then
|
|
res = res .. format_value('__mt', mt, indent)
|
|
first = false
|
|
end
|
|
|
|
-- add all values.
|
|
for k, v in pairs(tab) do
|
|
if not first then
|
|
res = res .. '\n'
|
|
end
|
|
|
|
res = res .. format_value(k, v, indent)
|
|
first = false
|
|
end
|
|
else
|
|
res = res .. format_value(nil, tab, indent)
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
|
|
--
|
|
-- Returns a copy of a list with all duplicate elements removed.
|
|
--
|
|
function table.unique(tab)
|
|
local elems = { }
|
|
local result = { }
|
|
table.foreachi(tab, function(elem)
|
|
if not elems[elem] then
|
|
table.insert(result, elem)
|
|
elems[elem] = true
|
|
end
|
|
end)
|
|
|
|
return result
|
|
end
|
|
|
|
--
|
|
-- Filters a table for empty entries. primarly useful for lists of string.
|
|
--
|
|
function table.filterempty(dirs)
|
|
return table.translate(dirs, function(val)
|
|
if val and #val > 0 then
|
|
return val
|
|
else
|
|
return nil
|
|
end
|
|
end)
|
|
end
|
|
|
|
|
|
--
|
|
-- Compares two tables.
|
|
--
|
|
function table.equals(a, b)
|
|
for k, v in pairs(a) do
|
|
if b[k] ~= v then
|
|
return false
|
|
end
|
|
end
|
|
for k, v in pairs(b) do
|
|
if a[k] ~= v then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
--
|
|
-- Enumerate a table sorted by its keys.
|
|
--
|
|
function spairs(t)
|
|
-- collect the keys
|
|
local keys = {}
|
|
for k in pairs(t) do
|
|
table.insert(keys, k)
|
|
end
|
|
table.sort(keys)
|
|
|
|
-- return the iterator function
|
|
local i = 0
|
|
return function()
|
|
i = i + 1
|
|
if keys[i] then
|
|
return keys[i], t[keys[i]]
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Intersect two arrays and return a new array
|
|
--
|
|
function table.intersect(a, b)
|
|
local result = {}
|
|
for _, v in ipairs(b) do
|
|
if table.indexof(a, v) then
|
|
table.insert(result, v)
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
--
|
|
-- The difference of A and B is the set containing those elements that are in A but not in B
|
|
--
|
|
function table.difference(a, b)
|
|
local result = {}
|
|
for _, v in ipairs(a) do
|
|
if not table.indexof(b, v) then
|
|
table.insert(result, v)
|
|
end
|
|
end
|
|
return result
|
|
end
|