Module:TableTools/sandbox: Difference between revisions

Content deleted Content added
Improved documentation formatting. Improved isArray and added isArrayLike. Applied Johnuniq's bugfix in _deepCopy.
implement fromIndex in inArray
 
(21 intermediate revisions by 5 users not shown)
Line 3:
-- --
-- This module includes a number of functions for dealing with Lua tables. --
-- It is a meta-module, meant to be called from other Lua modules, and should not --
-- not be called directly from #invoke. --
------------------------------------------------------------------------------------
 
Line 32:
-- isNan
--
-- This function returns true if the given number is a NaN value, and false if
-- if not. Although it doesn't operate on tables, it is included here as it is useful
-- useful for determining whether a value can be a valid table key. Lua will generate an
-- generate an error if a NaN is used as a table key.
------------------------------------------------------------------------------------
function p.isNan(v)
return type(v) == 'number' and tostring(v) =~= '-nan'v
end
 
Line 49:
------------------------------------------------------------------------------------
function p.shallowClone(t)
checkType('shallowClone', 1, t, 'table')
local ret = {}
for k, v in pairs(t) do
Line 63 ⟶ 64:
-- removed, but otherwise the array order is unchanged.
------------------------------------------------------------------------------------
function p.removeDuplicates(tarr)
checkType('removeDuplicates', 1, tarr, 'table')
local isNan = p.isNan
local ret, exists = {}, {}
for i_, v in ipairs(tarr) 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
elseif not exists[v] then
else
ifret[#ret not+ exists[v1] then= v
retexists[#ret + 1v] = vtrue
end
exists[v] = true
end
end
end
return ret
end
 
------------------------------------------------------------------------------------
Line 91 ⟶ 90:
local isPositiveInteger = p.isPositiveInteger
local nums = {}
for k, v in pairs(t) do
if isPositiveInteger(k) then
nums[#nums + 1] = k
Line 97 ⟶ 96:
end
table.sort(nums)
return nums
end
 
------------------------------------------------------------------------------------
-- reverseNumKeys
--
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in reverse numerical order.
------------------------------------------------------------------------------------
function p.reverseNumKeys(t)
checkType('reverseNumKeys', 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, function(a, b) return a > b end)
return nums
end
Line 124 ⟶ 104:
-- 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
-- return {1, 3, 6}.
------------------------------------------------------------------------------------
function p.affixNums(t, prefix, suffix)
Line 144 ⟶ 124:
 
local nums = {}
for k, v in pairs(t) do
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
if num then
Line 159 ⟶ 139:
-- 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
-- The compress option compresses the table so that it can be iterated over with
-- ipairs.
------------------------------------------------------------------------------------
Line 221 ⟶ 201:
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
 
------------------------------------------------------------------------------------
-- reverseSparseIpairs
--
-- This is a reverse iterator for sparse arrays. It can be used like a resersed
-- ipairs, but can handle nil values.
------------------------------------------------------------------------------------
function p.reverseSparseIpairs(t)
checkType('reverseSparseIpairs', 1, t, 'table')
local nums = p.reverseNumKeys(t)
local i = 0
local lim = #nums
Line 265 ⟶ 223:
checkType('size', 1, t, 'table')
local i = 0
for k_ in pairs(t) do
i = i + 1
end
Line 276 ⟶ 234:
if type1 ~= type2 then
return type1 < type2
elseelseif --type1 This== will'table' failor withtype1 table,== 'boolean,' or type1 == 'function.' then
return tostring(item1) < tostring(item2)
else
return item1 < item2
end
Line 283 ⟶ 243:
-- keysToList
--
-- Returns aan listarray of the keys in a table, sorted using either a default
-- comparison function or a custom keySort function.
------------------------------------------------------------------------------------
Line 289 ⟶ 249:
if not checked then
checkType('keysToList', 1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
end
 
local listarr = {}
local index = 1
for key, valuek in pairs(t) do
listarr[index] = keyk
index = index + 1
end
 
if keySort ~= false then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
table.sort(arr, keySort)
table.sort(list, keySort)
end
 
return listarr
end
 
Line 317 ⟶ 276:
checkType('sortedPairs', 1, t, 'table')
checkType('sortedPairs', 2, keySort, 'function', true)
 
local listarr = p.keysToList(t, keySort, true)
 
local i = 0
return function ()
i = i + 1
local key = listarr[i]
if key ~= nil then
return key, t[key]
Line 335 ⟶ 294:
-- isArray
--
-- Returns true if the given value is a table and all keys are consecutive integers
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArray(v)
Line 355 ⟶ 314:
-- isArrayLike
--
-- Returns true if the given value is iterable and all keys are consecutive integers
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArrayLike(v)
Line 375 ⟶ 334:
-- invert
--
-- Transposes the keys and values in an array. For example, {"a", "b", "c"} ->
-- For example, { "a", "b", "c" } -> { a = 1, b = 2, c = 3}. }Duplicates are not supported (result values refer to
-- the index of the last duplicate) and NaN values are ignored.
------------------------------------------------------------------------------------
function p.invert(arrayarr)
checkType("invert", 1, arrayarr, "table")
local isNan = p.isNan
local map = {}
for i, v in ipairs(arrayarr) do
if not isNan(v) then
map[v] = i
map[v] = i
end
end
 
return map
end
Line 393 ⟶ 355:
--
-- Creates a set from the array part of the table. Indexing the set by any of the
-- values of the array returns true. For example, {"a", "b", "c"} ->
-- For example, { "a", "b", "c" } -> { a = true, b = true, c = true}. }NaN values are ignored as Lua considers them
-- never equal to any value (including other NaNs or even themselves).
------------------------------------------------------------------------------------
function p.listToSet(tarr)
checkType("listToSet", 1, tarr, "table")
local isNan = p.isNan
local set = {}
for _, itemv in ipairs(tarr) do
if not isNan(v) then
set[item] = true
set[v] = true
end
end
 
return set
end
Line 413 ⟶ 378:
------------------------------------------------------------------------------------
local function _deepCopy(orig, includeMetatable, already_seen)
if type(orig) ~= "table" then
-- Stores copies of tables indexed by the original table.
return orig
already_seen = already_seen or {}
end
-- already_seen stores copies of tables indexed by the original table.
local copy = already_seen[orig]
if copy ~= nil then
Line 421 ⟶ 388:
end
copy = {}
if type(orig) == 'table' then
already_seen[orig] = copy -- memoize before any recursion, to avoid infinite loops
copy = {}
for orig_key, orig_value in pairs(orig) do
for orig_key, orig_value in pairs(orig) do
copy[_deepCopy(orig_key, includeMetatable, already_seen)] = _deepCopy(orig_value, includeMetatable, already_seen)
end
already_seen[orig] = copy
if includeMetatable then
local mt = getmetatable(orig)
if includeMetatable then
localif mt ~= getmetatable(orig)nil then
setmetatable(copy, _deepCopy(mt, true, already_seen))
if mt ~= nil then
local mt_copy = _deepCopy(mt, includeMetatable, already_seen)
setmetatable(copy, mt_copy)
already_seen[mt] = mt_copy
end
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
Line 444 ⟶ 407:
function p.deepCopy(orig, noMetatable, already_seen)
checkType("deepCopy", 3, already_seen, "table", true)
return _deepCopy(orig, not noMetatable, already_seen or {})
return _deepCopy(orig, not noMetatable, already_seen)
end
 
Line 452 ⟶ 414:
--
-- Concatenates all values in the table that are indexed by a number, in order.
-- sparseConcat{ a, nil, c, d } => "acd"
-- sparseConcat{ nil, b, c, d } => "bcd"
------------------------------------------------------------------------------------
function p.sparseConcat(t, sep, i, j)
local listarr = {}
 
local list_iarr_i = 0
for _, v in p.sparseIpairs(t) do
list_iarr_i = list_iarr_i + 1
listarr[list_iarr_i] = v
end
 
return table.concat(listarr, sep, i, j)
end
 
Line 479 ⟶ 441:
------------------------------------------------------------------------------------
function p.length(t, prefix)
-- requiring module inline so that [[Module:Exponential search]] which is
-- which is only needed by this one function doesn't get millions of transclusions
-- doesn't get millions of transclusions
local expSearch = require("Module:Exponential search")
checkType('length', 1, t, 'table')
checkType('length', 2, prefix, 'string', true)
return expSearch(function (i)
local key
if prefix then
Line 499 ⟶ 460:
-- inArray
--
-- Returns true if valueToFindsearchElement is a member of the array arr, and false otherwise.
-- Equivalent to JavaScript array.includes(searchElement) or
-- array.includes(searchElement, fromIndex), except fromIndex is 1 indexed
------------------------------------------------------------------------------------
function p.inArray(arrarray, valueToFindsearchElement, fromIndex)
checkType("inArray", 1, arrarray, "table")
-- if searchElement is nil, error?
 
-- if valueToFind is nil, error?
fromIndex = tonumber(fromIndex)
if fromIndex then
for _, v in ipairs(arr) do
if v(fromIndex ==< valueToFind0) then
fromIndex = #array + fromIndex + 1
return true
end
if fromIndex < 1 then fromIndex = 1 end
for _, v in ipairs({unpack(array, fromIndex)}) do
if v == searchElement then
return true
end
end
else
for _, v in pairs(array) do
if v == searchElement then
return true
end
end
end
return false
end
 
------------------------------------------------------------------------------------
-- merge
--
-- Given the arrays, returns an array containing the elements of each input array
-- in sequence.
------------------------------------------------------------------------------------
function p.merge(...)
local arrays = {...}
local ret = {}
for i, arr in ipairs(arrays) do
checkType('merge', i, arr, 'table')
for _, v in ipairs(arr) do
ret[#ret + 1] = v
end
end
return ret
end
 
------------------------------------------------------------------------------------
-- extend
--
-- Extends the first array in place by appending all elements from the second
-- array.
------------------------------------------------------------------------------------
function p.extend(arr1, arr2)
checkType('extend', 1, arr1, 'table')
checkType('extend', 2, arr2, 'table')
 
for _, v in ipairs(arr2) do
arr1[#arr1 + 1] = v
end
end