MicroController/stdlib/table.lua
2019-03-05 19:50:21 +08:00

312 lines
11 KiB
Lua

--- Table module
-- @module table
--- Given a mapping function, creates a transformed copy of the table
--- by calling the function for each element in the table, and using
--- the result as the new value for the key. Passes the index as second argument to the function.
--- @usage a= { 1, 2, 3, 4, 5}
---table.map(a, function(v) return v * 10 end) --produces: { 10, 20, 30, 40, 50 }
--- @usage a = {1, 2, 3, 4, 5}
---table.map(a, function(v, k, x) return v * k + x end, 100) --produces { 101, 104, 109, 116, 125}
-- @param tbl to be mapped to the transform
-- @param func to transform values
-- @param[opt] ... additional arguments passed to the function
-- @return a new table containing the keys and mapped values
function table.map(tbl, func, ...)
local newtbl = {}
for i, v in pairs(tbl) do
newtbl[i] = func(v, i, ...)
end
return newtbl
end
--- Given a filter function, creates a filtered copy of the table
--- by calling the function for each element in the table, and
--- filtering out any key-value pairs for non-true results. Passes the index as second argument to the function.
--- @usage a= { 1, 2, 3, 4, 5}
---table.filter(a, function(v) return v % 2 == 0 end) --produces: { 2, 4 }
--- @usage a = {1, 2, 3, 4, 5}
---table.filter(a, function(v, k, x) return k % 2 == 1 end) --produces: { 1, 3, 5 }
-- @param tbl to be filtered
-- @param func to filter values
-- @param[opt] ... additional arguments passed to the function
-- @return a new table containing the filtered key-value pairs
function table.filter(tbl, func, ...)
local newtbl = {}
local insert = #tbl > 0
for k, v in pairs(tbl) do
if func(v, k, ...) then
if insert then table.insert(newtbl, v)
else newtbl[k] = v end
end
end
return newtbl
end
--- Given a candidate search function, iterates over the table, calling the function
--- for each element in the table, and returns the first element the search function returned true.
--- Passes the index as second argument to the function.
--- @usage a= { 1, 2, 3, 4, 5}
---table.find(a, function(v) return v % 2 == 0 end) --produces: 2
--- @usage a = {1, 2, 3, 4, 5}
---table.find(a, function(v, k, x) return k % 2 == 1 end) --produces: 1
-- @param tbl to be searched
-- @param func to use to search for any matching element
-- @param[opt] ... additional arguments passed to the function
-- @return the first found value, or nil if none was found
function table.find(tbl, func, ...)
for k, v in pairs(tbl) do
if func(v, k, ...) then
return v, k
end
end
return nil
end
--- Given a candidate search function, iterates over the table, calling the function
--- for each element in the table, and returns true if search function returned true.
--- Passes the index as second argument to the function.
--- <br/> See also: table.find(...)
--- @usage a= { 1, 2, 3, 4, 5}
---table.any(a, function(v) return v % 2 == 0 end) --produces: true
--- @usage a = {1, 2, 3, 4, 5}
---table.any(a, function(v, k, x) return k % 2 == 1 end) --produces: true
-- @param tbl to be searched
-- @param func to use to search for any matching element
-- @param[opt] ... additional arguments passed to the function
-- @return true if an element was found, false if none was found
function table.any(tbl, func, ...)
return table.find(tbl, func, ...) ~= nil
end
--- Given a function, apply it to each element in the table. Passes the index as second argument to the function.
-- <p>Iteration is aborted if the applied function returns true for any element during iteration.
-- @param tbl to be iterated
-- @param func to apply to values
-- @param[opt] ... additional arguments passed to the function
-- @return the given table
function table.each(tbl, func, ...)
for k, v in pairs(tbl) do
if func(v, k, ...) then
break
end
end
return tbl
end
--- Returns a new table that is a one-dimensional flattening of this array (recursively).
-- For every element that is an table, extract its elements into the new array.
-- The optional level argument determines the level of recursion to flatten.
-- <p>Note: does not flatten associative elements, only arrays
-- @param tbl to be flattened
-- @param level (optional) recursive levels, or no limit to recursion if not supplied
-- @return a new table that represents the flattened contents of the given table
function table.flatten(tbl, level)
local flattened = {}
table.each(tbl, function(value)
if type(value) == "table" and #value > 0 then
if level then
if level > 0 then
table.merge(flattened, table.flatten(value, level - 1), true)
else
table.insert(flattened, value)
end
else
table.merge(flattened, table.flatten(value), true)
end
else
table.insert(flattened, value)
end
end)
return flattened
end
--- Given an array, returns the first element or nil if no element exists
-- @param tbl the array
-- @return the first element
function table.first(tbl)
return tbl[1]
end
--- Given an array, returns the last element or nil if no elements exist
-- @param tbl the array
-- @return the last element
function table.last(tbl)
local size = #tbl
if size == 0 then return nil end
return tbl[size]
end
--- Given an array of only numeric values, returns the minimum or nil if no element exists
-- @param tbl the array
-- @return the minimum value
function table.min(tbl)
if #tbl == 0 then return nil end
local min = tbl[1]
for _, num in pairs(tbl) do
min = num < min and num or min
end
return min
end
--- Given an array of only numeric values, returns the maximum or nil if no element exists
-- @param tbl the array
-- @return the maximum value
function table.max(tbl)
if #tbl == 0 then return nil end
local max = tbl[1]
for _, num in pairs(tbl) do
max = num > max and num or max
end
return max
end
--- Given an array of only numeric values, return the sum of all values, or 0 for empty arrays
-- @param tbl the array
-- @return the sum
function table.sum(tbl)
local sum = 0
for _, num in pairs(tbl) do
sum = sum + num
end
return sum
end
--- Given an array of only numeric values, returns the average or nil if no element exists
-- @param tbl the array
-- @return the average value
function table.avg(tbl)
local cnt = #tbl
return cnt ~= 0 and table.sum(tbl) / cnt or nil
end
--- Merges 2 tables, values from first get overwritten by second
--- @usage function some_func(x, y, args)
-- args = table.merge({option1=false}, args)
-- if opts.option1 == true then return x else return y end
-- end
-- some_func(1,2) --returns 2
-- some_func(1,2,{option1=true}) --returns 1
-- @param tblA first table
-- @param tblB second table
-- @param array_merge (optional: false) whether to merge the tables as arrays, or associatively
-- @return tblA with merged values from tblB
function table.merge(tblA, tblB, array_merge)
if not tblB then
return tblA
end
if array_merge then
for _, v in pairs(tblB) do
table.insert(tblA, v)
end
else
for k, v in pairs(tblB) do
tblA[k] = v
end
end
return tblA
end
-- copied from factorio/data/core/luablib/util.lua
--- Creates a deep copy of table, not coyping Factorio objects
-- @param object the table to copy
-- @return a copy of the table
function table.deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif object.__self then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
--- Returns a copy of all of the values in the table
-- @param tbl the table to copy the keys from, or an empty table if the tbl is nil
-- @param sorted (optional) whether to sort the keys (slower) or keep the random order from pairs()
-- @param as_string (optional) whether to try and parse the values as strings, or leave them as their existing type
-- @return an array with a copy of all the values in the table
function table.values(tbl,sorted,as_string)
if not tbl then return {} end
local valueset = {}
local n = 0
if as_string == true then --checking as_string /before/ looping is faster
for _,v in pairs(tbl) do n = n+1 ; valueset[n] = tostring(v) end
else
for _,v in pairs(tbl) do n = n+1 ; valueset[n] = v end
end
if sorted == true then
table.sort(valueset, function(x,y) --sorts tables with mixed index types.
local tx = type(x) == 'number'
local ty = type(y) == 'number'
if tx == ty then
return x < y and true or false --similar type can be compared
elseif tx == true then
return true --only x is a number and goes first
else
return false --only y is a number and goes first
end
end)
end
return valueset
end
--- Returns a copy of all of the keys in the table
-- @param tbl the table to copy the keys from, or an empty table if the tbl is nil
-- @param sorted (optional) whether to sort the keys (slower) or keep the random order from pairs()
-- @param as_string (optional) whether to try and parse the keys as strings, or leave them as their existing type
-- @return an array with a copy of all the keys in the table
function table.keys(tbl,sorted,as_string)
if not tbl then return {} end
local keyset = {}
local n = 0
if as_string == true then --checking as_string /before/ looping is faster
for k,_ in pairs(tbl) do n = n+1 ; keyset[n] = tostring(k) end
else
for k,_ in pairs(tbl) do n = n+1 ; keyset[n] = k end
end
if sorted == true then
table.sort(keyset, function(x,y) --sorts tables with mixed index types.
local tx = type(x) == 'number'
local ty = type(y) == 'number'
if tx == ty then
return x < y and true or false --similar type can be compared
elseif tx == true then
return true --only x is a number and goes first
else
return false --only y is a number and goes first
end
end)
end
return keyset
end
--- Removes keys from a table (sets them to nil)
-- @usage local a = {1, 2, 3, 4}
--table.remove_keys(a, {1,3} --returns {nil, 2, nil, 4}
-- @usage local b = {k1 = 1, k2 = 'foo', old_key = 'bar'}
--table.remove_keys(b, {'old_key'}) --returns {k1 = 1, k2 = 'foo'}
-- @param tbl the table to remove the keys from
-- @param keys array with the keys to remove
-- @return tbl without the specified keys
function table.remove_keys(tbl, keys)
for i=1, #keys do
tbl[keys[i]] = nil
end
return tbl
end