This module was made as a sandbox for the TableTools module. This documentation is kept to prevent redlinks.
-- <nowiki>
--------------------------------------------------------------------------------
-- This module includes a number of functions for dealing with Lua tables.
--
-- @see [[Wikipedia:Module:TableTools]] for a similar module.
--------------------------------------------------------------------------------
local p = {}
-- Define often-used variables and functions.
local floor = math.floor
local infinity = math.huge
local function makeMsg(name, index, msg)
return string.format('bad argument %s to %q (%s)',
type(index) == 'string' and '\"'..index..'\"' or '#'..index,
name,
msg
)
end
local function assertTrue(cond, name, index, msg, ...)
if cond then
-- Second mode
if type(index) == 'string' then
error(name..': '..string.format(index, msg, ...), 3)
else
error(makeMsg(name, index, string.format(msg, ...)), 3)
end
end
end
local function checkType(name, argIdx, arg, expectTypes, nilOk)
local function makeArgNumber(val)
return table.concat{ type(val) == 'number' and '#' or '\'', val, type(val) ~= "number" and '\'' or '' }
end
local argType = type(arg)
local t = {}
t.checkType = error
if type(expectTypes) == "string" then
expectTypes = { expectTypes }
end
if nilOk and arg == nil then
return
end
for _, v in ipairs(expectTypes) do
if argType == v:lower() then
return
end
end
local n = #expectTypes
local typeList
if n > 1 then
typeList = table.concat(expectTypes, '/')
else
typeList = expectTypes[1]
end
local msg = string.format("bad argument %s to '%s' (%s expected, got %s)",
makeArgNumber(argIdx),
name,
typeList,
type(arg)
)
t.checkType(msg, 3)
end
--------------------------------------------------------------------------------
-- Returns a new table with all parameters stored into keys 1, 2, etc. and with
-- a field `n` with the total number of parameters. Note that the resulting
-- table may not be a sequence.
--
-- @see <http://www.lua.org/manual/5.2/manual.html#pdf-table.pack>
--------------------------------------------------------------------------------
function p.pack(...)
return {n = select('#', ...), ...}
end
--------------------------------------------------------------------------------
-- Returns the first `n` arguments in `...`. If `n` is negative, arguments are
-- counted from the end of the table.
--
-- @see [[Lua reference manual/Standard libraries#select]]
-- @see <http://lua-users.org/wiki/VarargTheSecondClassCitizen>
--------------------------------------------------------------------------------
function p.selectFirst(n, ...)
checkType('Dev:TableTools.selectFirst', 1, n, 'number')
local function err()
error(makeMsg('Dev:TableTools.selectFirst', 1, 'index out of range'), 3)
end
local function recurse(index, next, ...)
if index == 0 then
return
end
return next, recurse(index - 1, ...)
end
n = math.modf(n)
local count = select("#", ...)
if -count > n then
err()
elseif -1 > n and n >= -count then
return recurse(count + 1 + n, ...)
elseif n == -1 then
return ...
elseif n == 0 then
err()
elseif n == 1 then
return (...)
elseif 1 < n and n <= count then
return recurse(n, ...)
elseif count < n then
return ...
end
end
--------------------------------------------------------------------------------
-- Returns `true` if a given table is a sequence.
--
-- @see <http://stackoverflow.com/a/6080274>
--------------------------------------------------------------------------------
function p.isSequence(t)
checkType('Dev:TableTools.isSequence', 1, t, 'table')
local i = 1
for _ in pairs(t) do
if t[i] == nil then
return false
end
i = i + 1
end
return true
end
--------------------------------------------------------------------------------
-- Returns the number of elements in a table, even if it is not a sequence.
--
-- @see <http://stackoverflow.com/a/2705804>
--------------------------------------------------------------------------------
function p.size(t, countNamed)
checkType('Dev:TableTools.size', 1, t, 'table')
checkType('Dev:TableTools.size', 2, countNamed, 'boolean', true)
local i = 0
for k in pairs(t) do
if not countedNamed and countedNamed ~= nil then
if type(k) == "number" then
i = i + 1
end
else
i = i + 1
end
end
return i
end
---------------------------------------------------------------------------------
-- function: setPrototype(t: table, proto: table, indexFunc: function)
--
-- Sets up a metatable prototype with a table.
---------------------------------------------------------------------------------
function p.setPrototype(t, proto, indexFunc, parentProto)
local fName = 'Dev:TableTools.setPrototype'
checkType(fName, 1, t, 'table')
checkType(fName, 2, proto, 'table')
checkType(fName, 3, indexFunc, 'function', true)
checkType(fName, 4, parentProto, 'table', true)
local mt = getmetatable(t) or {}
assertTrue(mt.__index, fName, 1, 'cannot overwrite existing `__index` metafield on a table with an existing metatable')
mt.__proto = proto or {}
parentProto = parentProto or {}
if p.size(parentProto) ~= 0 then
mt.__proto.prototype = parentProto
end
indexFunc = indexFunc or function(t, k, proto, parentProto)
if k == 'prototype' then
return mt.__proto
else
return mt.__proto[k] or (p.size(parentProto or {}) ~= 0 and mt.__proto.prototype[k] or nil)
end
end
mt.__index = function(t, k)
return indexFunc(t, k, mt.__proto, mt.__proto.prototype)
end
return setmetatable(t, mt)
end
---------------------------------------------------------------------------------
-- function: sequenceToSet(t: table)
--
-- Creates a shallow copy of `t`. This means any subtables and functions will be shared.
-- Use `deepCopy()` for a deep copy function.
---------------------------------------------------------------------------------
function p.sequenceToSet(t)
checkType('Dev:TableTools.sequenceToSet', 1, t, 'table')
customArgError(not p.isSequence(t), 'Dev:TableTools.sequenceToSet', 1, 'table is not a sequence')
local ret = {}
p.each(t, function(v)
ret[v] = true
end)
return ret
end
--------------------------------------------------------------------------------
-- Returns `true` if a given table contains a certain element.
--
-- @see <http://stackoverflow.com/q/2282444>
--------------------------------------------------------------------------------
function p.includes(t, v)
checkType('Dev:TableTools.includes', 1, t, 'table')
assertTrue(not p.isSequence(t), 'Dev:TableTools.includes', 1, 'provided table is not a sequence')
return p.some(t,
function(i, v)
return t[i] == v
end
)
end
--------------------------------------------------------------------------------
-- Merges the content of the second table with the content in the first one.
--
-- @see <http://wiki.garrysmod.com/page/table/Merge>
--------------------------------------------------------------------------------
function p.merge(dest, source)
checkType('Dev:TableTools.merge', 1, dest, 'table')
checkType('Dev:TableTools.merge', 2, source, 'table')
for k, v in pairs(source) do
if type(v) == 'table' and type(dest[k]) == 'table' then
-- Don't overwrite one table with another; instead merge them
-- recurisvely.
p.merge(dest[k], v)
else
dest[k] = v
end
end
return dest
end
---------------------------------------------------------------------------------
-- function: unshift(t: table, ...items: any)
--
-- Prepends each of `...items` to the front of `t`.
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.unshift(t, ...)
local items = p.pack(...)
local fName = 'Dev:TableTools.unshift'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
for _, v in ipairs(items) do
table.insert(t, 1, v)
end
return t
end
---------------------------------------------------------------------------------
-- function: push(t: table, ...items: any)
--
-- Appends each of `...items` to the end of `t`.
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.push(t, ...)
local items = p.pack(...)
local fName = 'Dev:TableTools.push'
checkType('push', 1, t, 'table')
assertTrue(not p.isSequence(t), 'push', 1, 'provided table is not a sequence')
for _, v in ipairs(items) do
table.insert(t, v)
end
return t
end
---------------------------------------------------------------------------------
-- function: map(t: table, callbackfn: function(v: any, i?: number, t?: table))
--
-- Creates a new table and populates with results from the callback function.
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
--
---------------[ CALLBACK PARAMETERS ]-------------
-- The callback to `map` has three values passed to it described above.
-- `v`, the value of the table index, `i`, the table index, and `t`, the actual table.
-- `map` will popluate the new table with the return values of this function.
---------------------------------------------------------------------------------
function p.map(t, callbackfn)
local fName = 'Dev:TableTools.map'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
checkType(fName, 2, callbackfn, 'function')
local ret = {}
for i, v in ipairs(t) do
p.push(ret, callbackfn(i, v, t))
end
return ret
end
---------------------------------------------------------------------------------
-- function: filter(t: table, callbackfn: function(v: any, i?: number, t?: table))
--
-- Creates a new table with all elements that pass the test implemented by the provided function.
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
--
---------------[ CALLBACK PARAMETERS ]-------------
-- callbackfn(v: any, i?: number, t?: table)
--
-- The callback to `filter` has three values passed to it described above.
-- `v`, the value of the table index, `i`, the table index, and `t`, the actual table.
-- `filter` will popluate the new table with the value of the old table if the returned value
-- from the callback is true.
---------------------------------------------------------------------------------
function p.filter(t, callbackfn)
local fName = 'Dev:TableTools.filter'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
checkType(fName, 2, callbackfn, 'function')
local ret = {}
for i, v in ipairs(t) do
if callbackfn(v, i, t) then
p.push(ret, v)
end
end
return ret
end
---------------------------------------------------------------------------------
-- function: removeLast(t: table)
--
-- Removes the last element of the table and returns it.
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.removeLast(t)
local fName = 'Dev:TableTools.removeLast'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
return table.remove(t, p.size(t))
end
---------------------------------------------------------------------------------
-- function: removeFirst(t: table)
--
-- Removes the First element of the table and returns it.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.removeFirst(t)
local fName = 'Dev:TableTools.removeFirst'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
return table.remove(t, 1)
end
---------------------------------------------------------------------------------
-- function: fill(t?: table, v: any, startIndex: number, endIndex?: number)
--
-- Sets the table index starting at `startIndex` with the value of `v` ending at `endIndex`.
-- If end is not specified, it defualts to the length of the table.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.fill(t, v, startIndex, endIndex)
local fName = 'Dev:TableTools.fill'
local t = t or {}
checkType(fName, 1, t, 'table', true)
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
checkType(fName, 3, startIndex, 'number')
checkType(fName, 4, endIndex, 'number', true)
for i = startIndex, endIndex or #t, 1 do
t[i] = v
end
return t
end
---------------------------------------------------------------------------------
-- function: indexOf(t: table, v: any)
--
-- Searches for `v` the first index in `t`. If nothing is found, it returns `-1`.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.indexOf(t, value)
checkType('Dev:TableTools.indexOf', 1, t, 'table')
local index
p.some(t, function(k, v)
if v == value then
index = k
return true
else
return false
end
end)
return index or -1
end
---------------------------------------------------------------------------------
-- function: lastIndexOf(t: table, v: any)
--
-- Searches for `v` the first index in `t`. If nothing is found, it returns `-1`.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
---------------------------------------------------------------------------------
function p.lastIndexOf(t, v)
checkType('Dev:TableTools.lastIndexOf', 1, t, 'table')
assertTrue(not p.isSequence(t), 'Dev:TableTools.lastIndexOf', 1, 'provided table is not a sequence')
for i, value in p.reverseIpairs(t) do
if v == value then return i end
end
return -1
end
---------------------------------------------------------------------------------
-- function: every(t: table, callbackfn: function(k: any, v?: any, t?: table, i?: number) => check)
--
-- Tests every element from the return value from `callbackfn`. If any elements fail
-- the test, it returns false.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
--
---------------[ CALLBACK PARAMETERS ]-------------
-- callbackfn(k: any, v?: any, t?: table, i?: number)
--
-- If the callback returns false, `every()` will consider the test failed and
-- return false.
-- *`k` is the table key `every()` is currently over.
-- *`v` is the value of the table key `every()` is currently over.
-- *`t` is the table `every()` was called on.
-- *`i` is the number of iterations `every()` has iterated over.
---------------------------------------------------------------------------------
function p.every(t, callbackfn)
checkType('Dev:TableTools.every', 1, t, 'table')
checkType('Dev:TableTools.every', 2, callbackfn, 'function')
local i = 0
for k, v in pairs(t) do
i = i + 1
if not callbackfn(k, v, t, i) then
return false
end
end
return true
end
---------------------------------------------------------------------------------
-- function: some(t: table, callbackfn: function(v: any, i: number, t: table) => check)
--
-- Tests whether at least one element in the table passes the test implemented
-- by the provided function. It returns a Boolean value.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
----------------------------------------------------------------------------------
function p.some(t, callbackfn)
checkType('Dev:TableTools.some', 1, t, 'table')
checkType('Dev:TableTools.some', 2, callbackfn, 'function')
local i = 0
for k, v in pairs(t) do
i = i + 1
if callbackfn(k, v, t, i) then
return true
end
end
return false
end
---------------------------------------------------------------------------------
-- function: reduce(
-- t: table,
-- callbackfn: function(accumlator: any, curVal: any, i?: number, t?: table) => accumlator,
-- initVal: any
--)
--
-- Executes a reducer callback function on each element of the table, resulting in single output value.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
--
---------------[ CALLBACK PARAMETERS ]-------------
-- callbackfn(accumlator: any, curVal: any, i?: number, t?: table)
--
-- The callback to `every` has four values passed to it described above.
-- `accumlator` is the accumulated value previously returned in the last invocation
-- of the callback value of to accumalate.
-- `curVal` is the current element being processed in the table.
-- `i` is the current index of the processes element.
-- `t` is the table `reduce()` was called on.
---------------------------------------------------------------------------------
function p.reduce(t, callbackfn, initVal)
local fName = 'Dev:TableTools.reduce'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
checkType(fName, 2, callbackfn, 'function')
assertTrue(callbackfn('', '', '', '') == nil, fName, 1, 'no return value for callback')
local accumulator
for i, v in pairs(t) do
if i == 1 then
accumulator = initVal and callbackfn(initVal, v, i, t) or v
elseif i ~= 1 then
accumulator = callbackfn(accumulator, v, i, t)
end
end
return accumulator
end
---------------------------------------------------------------------------------
-- function: reduceRight(
-- t: table,
-- callbackfn: function(accumlator: any, curVal: any, i?: number, t?: table),
-- initVal: any
--)
--
-- Executes a reducer callback function on each element of the table from left to right,
-- resulting in single output value. Very similar to `reduce()`.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
--
---------------[ CALLBACK PARAMETERS ]-------------
-- callbackfn(accumlator: any, curVal: any, i?: number, t?: table)
--
-- The callback to `every` has four values passed to it described above.
-- `accumlator` is the accumulated value previously returned in the last invocation
-- of the callback value of to accumalate.
-- `curVal` is the current element being processed in the table.
-- `i` is the current index of the processes element.
-- `t` is the table `reduceRight()` was called on.
---------------------------------------------------------------------------------
function p.reduceRight(t, callbackfn, initVal)
local fName = 'Dev:TableTools.reduceRight'
checkType(fName, 1, t, 'table')
assertTrue(not p.isSequence(t), fName, 1, 'provided table is not a sequence')
checkType(fName, 2, callbackfn, 'function')
assertTrue(callbackfn('', '', '', '') == nil, fName, 2, 'no return value for callback')
local accumulator
for i, v, start in p.reverseIpairs(t) do
if i == start then
accumulator = initVal and callbackfn(initVal, v, i, t) or v
elseif i ~= start then
accumulator = callbackfn(accumulator, v, i, t)
end
end
return accumulator
end
---------------------------------------------------------------------------------
-- function: reverseIpairs(t: table)
--
-- Returns a iterator function to iterate backwards over a sequence table.
-- This works like `ipairs()` except it works backwards, and it provides an additional
-- value in the iteration, `start`. The `start` value is the index the function
-- started iterating at.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table>
--
-----------------[ EXAMPLE ]----------------
-- for i, v, start in TableTools.reverseIpairs(t) do
-- -- code block
-- end
---------------------------------------------------------------------------------
function p.reverseIpairs(t)
checkType('Dev:TableTools.reverseIpairs', 1, t, 'table')
assertTrue(not p.isSequence(t), 'Dev:TableTools.reverseIpairs', 1, 'provided table is not a sequence')
return function(a, i)
checkType('?', 2, i, 'number')
checkType('?', 1, a, 'table')
i = i - 1
local v = a[i]
if v then
return i, v, p.size(t)
end
end, t, p.size(t)+1
end
---------------------------------------------------------------------------------
-- function: reverse(t: table)
--
-- Reverses the table in place. The first array element becomes the last, and
-- the last array element becomes the first.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table
---------------------------------------------------------------------------------
function p.reverse(t)
checkType('Dev:TableTools.reverse', 1, t, 'table')
assertTrue(not p.isSequence(t), 'Dev:TableTools.reverse', 1, 'provided table is not a sequence')
local tmp = {}
for _, v in p.reverseIpairs(t) do
p.push(tmp, v)
end
p.empty(t)
for i, v in ipairs(tmp) do
t[i] = v
end
return t
end
---------------------------------------------------------------------------------
-- function: empty(t: table)
--
-- Empties the table of all keys.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table
---------------------------------------------------------------------------------
function p.empty(t)
checkType('Dev:TableTools.empty', 1, t, 'table')
for k, v in pairs(t) do
t[k] = nil
end
return t
end
---------------------------------------------------------------------------------
-- function: tail(t: table)
--
-- Moves the first key from the table to the end of it.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table
---------------------------------------------------------------------------------
function p.tail(t)
checkType('Dev:TableTools.tail', 1, t, 'table')
p.push(t, p.removeFirst(t))
return t
end
---------------------------------------------------------------------------------
-- function: untail(t: table)
--
-- Moves the last key from the table to the front of it.
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table
---------------------------------------------------------------------------------
function p.untail(t)
checkType('Dev:TableTools.untail', 1, t, 'table')
p.unshift(t, p.removeLast(t))
return t
end
------------------------------------------------------------------------------------
-- isPositiveInteger
--
-- This function returns true if the given value is a positive integer, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a given table key is in the array part or the
-- hash part of a table.
------------------------------------------------------------------------------------
function p.isPositiveInteger(v)
if type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity then
return true
else
return false
end
end
------------------------------------------------------------------------------------
-- isNan
--
-- This function returns true if the given number is a NaN value, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a value can be a valid table key. Lua will
-- generate an error if a NaN is used as a table key.
------------------------------------------------------------------------------------
function p.isNan(v)
if type(v) == 'number' and tostring(v) == '-nan' then
return true
else
return false
end
end
------------------------------------------------------------------------------------
-- shallowClone
--
-- This returns a clone of a table. The value returned is a new table, but all
-- subtables and functions are shared. Metamethods are respected, but the returned
-- table will have no metatable of its own.
------------------------------------------------------------------------------------
function p.shallowClone(t)
checkType('Dev:TableTools.shallowClone', 1, t, 'table')
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
------------------------------------------------------------------------------------
-- removeDuplicates
--
-- This removes duplicate values from an array. Non-positive-integer keys are
-- ignored. The earliest value is kept, and all subsequent duplicate values are
-- removed, but otherwise the array order is unchanged.
------------------------------------------------------------------------------------
function p.removeDuplicates(t)
checkType('removeDuplicates', 1, t, 'table')
local isNan = p.isNan
local ret, exists = {}, {}
for i, v in ipairs(t) do
if isNan(v) then
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
ret[#ret + 1] = v
else
if not exists[v] then
ret[#ret + 1] = v
exists[v] = true
end
end
end
return ret
end
------------------------------------------------------------------------------------
-- numKeys
--
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in numerical order.
------------------------------------------------------------------------------------
function p.numKeys(t)
checkType('numKeys', 1, t, 'table')
local isPositiveInteger = p.isPositiveInteger
local nums = {}
for k, v in pairs(t) do
if isPositiveInteger(k) then
nums[#nums + 1] = k
end
end
table.sort(nums)
return nums
end
------------------------------------------------------------------------------------
-- affixNums
--
-- This takes a table and returns an array containing the numbers of keys with the
-- specified prefix and suffix. For example, for the table
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will
-- return {1, 3, 6}.
------------------------------------------------------------------------------------
function p.affixNums(t, prefix, suffix)
checkType('affixNums', 1, t, 'table')
checkType('affixNums', 2, prefix, 'string', true)
checkType('affixNums', 3, suffix, 'string', true)
local function cleanPattern(s)
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
s = s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
return s
end
prefix = prefix or ''
suffix = suffix or ''
prefix = cleanPattern(prefix)
suffix = cleanPattern(suffix)
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
local nums = {}
for k, v in pairs(t) do
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
if num then
nums[#nums + 1] = tonumber(num)
end
end
end
table.sort(nums)
return nums
end
------------------------------------------------------------------------------------
-- numData
--
-- Given a table with keys like ("foo1", "bar1", "foo2", "baz2"), returns a table
-- of subtables in the format
-- { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }
-- Keys that don't end with an integer are stored in a subtable named "other".
-- The compress option compresses the table so that it can be iterated over with
-- ipairs.
------------------------------------------------------------------------------------
function p.numData(t, compress)
checkType('numData', 1, t, 'table')
checkType('numData', 2, compress, 'boolean', true)
local ret = {}
for k, v in pairs(t) do
local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
if num then
num = tonumber(num)
local subtable = ret[num] or {}
if prefix == '' then
-- Positional parameters match the blank string; put them at the start of the subtable instead.
prefix = 1
end
subtable[prefix] = v
ret[num] = subtable
else
local subtable = ret.other or {}
subtable[k] = v
ret.other = subtable
end
end
if compress then
local other = ret.other
ret = p.compressSparseArray(ret)
ret.other = other
end
return ret
end
------------------------------------------------------------------------------------
-- compressSparseArray
--
-- This takes an array with one or more nil values, and removes the nil values
-- while preserving the order, so that the array can be safely traversed with
-- ipairs.
------------------------------------------------------------------------------------
function p.compressSparseArray(t)
checkType('compressSparseArray', 1, t, 'table')
local ret = {}
local nums = p.numKeys(t)
for _, num in ipairs(nums) do
ret[#ret + 1] = t[num]
end
return ret
end
------------------------------------------------------------------------------------
-- sparseIpairs
--
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
-- handle nil values.
------------------------------------------------------------------------------------
function p.sparseIpairs(t)
checkType('sparseIpairs', 1, t, 'table')
local nums = p.numKeys(t)
local i = 0
local lim = #nums
return function ()
i = i + 1
if i <= lim then
local key = nums[i]
return key, t[key]
else
return nil, nil
end
end
end
---------------------------------------------------------------------------------
-- function: each(t: table, callbackfn: function(k: any, v?: any, t?: table, i?: number)
--
-- Executes a provided function once for each array element.
---------------------------------------------------------------------------------
function p.each(t, callbackfn)
checkType('Dev:TableTools.each', 1, t, 'table')
checkType('Dev:TableTools.each', 2, callbackfn, "function")
local i = 0
for k, v in p.sortedPairs(t) do
callbackfn(v, k, t, i)
i = i + 1
end
return t
end
---------------------------------------------------------------------------------
-- function: format(t: table)
--
-- Takes the table and takes each value as an argument and puts it through `string.format()`.
---------------------------------------------------------------------------------
function p.format(t)
checkType('Dev:TableTools.format', 1, t, { 'table', 'string' }, true)
if type(t) == 'string' or t == '' or t == nil then return t end
return string.format(unpack(t))
end
---------------------------------------------------------------------------------
-- function: tableUtil(t: table, mt?: table)
--
-- Takes all functions from above and any native functions relating to tables and
-- makes them methods on the returned table
--
-- @see <https://hypixel-skyblock.fandom.com/wiki/Module:Table
---------------------------------------------------------------------------------
function p.tableUtil(t, mt)
checkType('Dev:TableTools.tableUtil', 1, t, 'table', true)
checkType('Dev:TableTools.tableUtil', 2, mt, 'table', true)
t, mt = t or {}, mt or {}
origMt = getmetatable(t)
p.merge(mt, origMt or {})
indexField = mt.__index
mt.__index = nil
setmetatable(t, mt)
tmp = p.merge(p, table)
local others = {
rawget = rawget,
rawset = rawset,
ipairs = ipairs,
pairs = pairs,
next = next,
tonumber = tonumber,
tostring = tostring,
type = type,
print = mw.log,
log = mw.log,
getmetatable = getmetatable,
}
p.merge(tmp, others)
return p.setPrototype(t, tmp, indexField)
end
return p
-- </nowiki>
-- (Add categories here.)