Module:table: Verskil tussen weergawes

Content deleted Content added
update manually from mw
Naudefj (Besprekings | bydraes)
No edit summary
 
Lyn 1:
--[[
------------------------------------------------------------------------------------
-- table (formerly TableTools ) --
-- --
-- This module includesinclcudes 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 be called directly from #invoke. --
------------------------------------------------------------------------------------
--]]
 
--[[
Inserting new values into a table using a local "index" variable, which is
incremented each time, is faster than using "table.insert(t, x)" or
"t[#t + 1] = x". See the talk page.
]]
 
local libraryUtil = require('libraryUtil')
 
local pexport = {}
 
-- Define often-used variables and functions.
Line 18 ⟶ 24:
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
 
local function _check(funcName, expectType)
if type(expectType) == "string" then
return function(argIndex, arg, nilOk)
checkType(funcName, argIndex, arg, expectType, nilOk)
end
else
return function(argIndex, arg, expectType, nilOk)
if type(expectType) == "table" then
checkTypeMulti(funcName, argIndex, arg, expectType, nilOk)
else
checkType(funcName, argIndex, arg, expectType, nilOk)
end
end
end
end
 
--[[
Line 29 ⟶ 51:
------------------------------------------------------------------------------------
--]]
function pexport.isPositiveInteger(v)
ifreturn type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity then
return true
else
return false
end
end
 
Line 47 ⟶ 65:
------------------------------------------------------------------------------------
--]]
function pexport.isNan(v)
if type(v) == 'number' and tostring(v) == '-nan' then
return true
Line 64 ⟶ 82:
------------------------------------------------------------------------------------
--]]
function pexport.shallowClone(t)
local ret = {}
for k, v in pairs(t) do
Line 70 ⟶ 88:
end
return ret
end
 
--[[
Shallow copy
]]
function export.shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
 
--[[
Recursive deep copy function
Equivalent to mw.clone?
]]
local function deepcopy(orig, includeMetatable, already_seen)
-- Stores copies of tables indexed by the original table.
already_seen = already_seen or {}
local copy = already_seen[orig]
if copy ~= nil then
return copy
end
if type(orig) == 'table' then
copy = {}
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 mt ~= nil then
local mt_copy = deepcopy(mt, includeMetatable, already_seen)
setmetatable(copy, mt_copy)
end
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
 
function export.deepcopy(orig, noMetatable, already_seen)
checkType("deepcopy", 3, already_seen, "table", true)
return deepcopy(orig, not noMetatable, already_seen)
end
 
Line 81 ⟶ 155:
------------------------------------------------------------------------------------
--]]
function pexport.removeDuplicates(t)
checkType('removeDuplicates', 1, t, 'table')
local isNan = pexport.isNan
local ret, exists = {}, {}
local index = 1
for i, v in ipairs(t) do
for _, 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 + 1index] = v
index = index + 1
else
if not exists[v] then
ret[#ret + 1index] = v
index = index + 1
exists[v] = true
end
end
end
return ret
end
 
--[[
Line 107 ⟶ 184:
------------------------------------------------------------------------------------
--]]
function pexport.numKeys(t, checked)
if not checked then
checkType('numKeys', 1, t, 'table')
checkType('numKeys', 1, t, 'table')
local isPositiveInteger = p.isPositiveInteger
end
local isPositiveInteger = export.isPositiveInteger
local nums = {}
local index = 1
for k, v in pairs(t) do
for k, _ in pairs(t) do
if isPositiveInteger(k) then
nums[#nums + 1index] = k
index = index + 1
end
end
table.sort(nums)
return nums
end
 
function export.maxIndex(t)
checkType('maxIndex', 1, t, 'table')
local positiveIntegerKeys = export.numKeys(t)
if positiveIntegerKeys[1] then
return math.max(unpack(positiveIntegerKeys))
else
return 0 -- ???
end
end
 
Line 125 ⟶ 216:
--
-- This takes a table and returns an array containing the numbers of keys with the
-- specified prefix and suffix. For example, for the table
-- affixNums({a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix, "a", affixNums will)
-- ↓
-- return {1, 3, 6}.
-- {1, 3, 6}.
------------------------------------------------------------------------------------
--]]
function pexport.affixNums(t, prefix, suffix)
local check = _check('affixNums')
checkType('affixNums', 1, t, 'table')
check(1, t, 'table')
checkType('affixNums', 2, prefix, 'string', true)
checkTypecheck('affixNums'2, 3, suffixprefix, 'string', true)
check(3, suffix, 'string', true)
 
local function cleanPattern(s)
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
Line 140 ⟶ 233:
return s
end
 
prefix = prefix or ''
suffix = suffix or ''
Line 146 ⟶ 239:
suffix = cleanPattern(suffix)
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
 
local nums = {}
local index = 1
for k, v in pairs(t) do
for k, _ in pairs(t) do
if type(k) == 'string' then
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
if num then
nums[#nums + 1index] = tonumber(num)
index = index + 1
end
end
Line 165 ⟶ 260:
--
-- 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".
Line 172 ⟶ 267:
------------------------------------------------------------------------------------
--]]
function pexport.numData(t, compress)
local check = _check('numData')
checkType('numData', 1, t, 'table')
check(1, t, 'table')
checkType('numData', 2, compress, 'boolean', true)
check(2, compress, 'boolean', true)
local ret = {}
for k, v in pairs(t) do
local prefix, num = mw.ustring.match(tostring(k), :match('^([^0-9]*)([1-9][0-9]*)$')
if num then
num = tonumber(num)
Line 195 ⟶ 292:
if compress then
local other = ret.other
ret = pexport.compressSparseArray(ret)
ret.other = other
end
Line 210 ⟶ 307:
------------------------------------------------------------------------------------
--]]
function pexport.compressSparseArray(t)
checkType('compressSparseArray', 1, t, 'table')
local ret = {}
local numsindex = p.numKeys(t)1
local nums = export.numKeys(t)
for _, num in ipairs(nums) do
ret[#ret + 1index] = t[num]
index = index + 1
end
return ret
Line 228 ⟶ 327:
------------------------------------------------------------------------------------
--]]
function pexport.sparseIpairs(t)
checkType('sparseIpairs', 1, t, 'table')
local nums = pexport.numKeys(t)
local i = 0
return function()
local lim = #nums
return function ()
i = i + 1
iflocal ikey <= lim thennums[i]
localif key = nums[i]then
return key, t[key]
else
Line 252 ⟶ 350:
------------------------------------------------------------------------------------
--]]
function export.size(t)
 
function p.size(t)
checkType('size', 1, t, 'table')
local i = 0
for k_ in pairs(t) do
i = i + 1
end
Line 262 ⟶ 359:
end
 
--[[
-- This returns the length of a table, or the first integer key n counting from
-- 1 such that t[n + 1] is nil. It is similar to the operator #, but may return
-- a different value when there are gaps in the array portion of the table.
-- Intended to be used on data loaded with mw.loadData. For other tables, use #.
--]]
function export.length(t)
local i = 0
repeat
i = i + 1
until t[i] == nil
return i - 1
end
 
--[[
local function defaultKeySort(item1, item2)
Takes table and a value to be found.
If the value is in the array portion of the table, return true.
If the value is in the hashmap or not in the table, return false.
]]
function export.contains(list, x)
for _, v in ipairs(list) do
if v == x then return true end
end
return false
end
 
--[[
Recursively compare two values that may be tables, including tables with
nested tables as values. Return true if both values are structurally equal.
Note that this handles arbitary levels of nesting. If all tables are known
to be lists (with only integral keys), use export.deepEqualsList, which will
be more efficient.
 
NOTE: This is *NOT* smart enough to properly handle cycles; in such a case, it
will get into an infinite loop.
]]
function export.deepEquals(x, y)
if type(x) == "table" and type(y) == "table" then
-- Two tables are the same if they have the same number of elements
-- and all keys that are present in one of the tables compare equal
-- to the corresponding keys in the other table, using structural
-- comparison.
local sizex = 0
for key, value in pairs(x) do
if not export.deepEquals(value, y[key]) then
return false
end
sizex = sizex + 1
end
local sizey = export.size(y)
if sizex ~= sizey then
return false
end
return true
end
return x == y
end
 
--[[
Recursively compare two values that may be lists (i.e. tables with integral
keys), including lists with nested lists as values. Return true if both values
are structurally equal. Note that this handles arbitary levels of nesting.
Results are undefined if tables with non-integral keys are present anywhere in
either structure; if that may be the case, use export.deepEquals, which will
handle such tables correctly but be less efficient on lists than
export.deepEqualsList.
 
NOTE: This is *NOT* smart enough to properly handle cycles; in such a case, it
will get into an infinite loop.
]]
function export.deepEqualsList(x, y)
if type(x) == "table" and type(y) == "table" then
if #x ~= #y then
return false
end
for key, value in ipairs(x) do
if not export.deepEqualsList(value, y[key]) then
return false
end
end
return true
end
return x == y
end
 
--[[
Finds key for specified value in a given table.
Roughly equivalent to reversing the key-value pairs in the table –
reversed_table = { [value1] = key1, [value2] = key2, ... }
– and then returning reversed_table[valueToFind].
The value can only be a string or a number
(not nil, a boolean, a table, or a function).
Only reliable if there is just one key with the specified value.
Otherwise, the function returns the first key found,
and the output is unpredictable.
]]
function export.keyFor(t, valueToFind)
local check = _check('keyFor')
check(1, t, 'table')
check(2, valueToFind, { 'string', 'number' })
for key, value in pairs(t) do
if value == valueToFind then
return key
end
end
return nil
end
 
--[[
The default sorting function used in export.keysToList if no keySort
is defined.
]]
local function defaultKeySort(key1, key2)
-- "number" < "string", so numbers will be sorted before strings.
local type1, type2 = type(item1key1), type(item2key2)
if type1 ~= type2 then
return type1 < type2
else
else -- This will fail with table, boolean, function.
return item1key1 < item2key2
end
end
 
--[[
Returns a list of the keys in a table, sorted using either athe default
comparisontable.sort function or a custom keySort function.
If there are only numerical keys, numKeys is probably more efficient.
]]
function pexport.keysToList(t, keySort, checked)
if not checked then
local check = _check('keysToList')
checkType('keysToList', 1, t, 'table')
check(1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
check(2, keySort, 'function', true)
end
local list = {}
local index = 1
for key, value_ in pairs(t) do
list[index] = key
index = index + 1
end
-- Place numbers before strings, otherwise sort using <.
if keySort ~= false then
if not keySort then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
keySort = defaultKeySort
table.sort(list, keySort)
end
table.sort(list, keySort)
return list
Line 303 ⟶ 518:
If there are only numerical keys, sparseIpairs is probably more efficient.
]]
function pexport.sortedPairs(t, keySort)
local check = _check('keysToList')
checkType('sortedPairs', 1, t, 'table')
check(1, t, 'table')
checkType('sortedPairs', 2, keySort, 'function', true)
check(2, keySort, 'function', true)
local list = pexport.keysToList(t, keySort, true)
local i = 0
Line 321 ⟶ 537:
end
 
function export.reverseIpairs(list)
--[[
checkType('reverse_ipairs', 1, list, 'table')
Returns true if all keys in the table are consecutive integers starting at 1.
--]]
function p.isArray(t)
checkType("isArray", 1, t, "table")
local i = 0#list + 1
return function()
for k, v in pairs(t) do
i = i +- 1
if tlist[i] =~= nil then
return falsei, list[i]
else
return nil, nil
end
end
return true
end
 
--[=[
-- { "a", "b", "c" } -> { a = 1, b = 2, c = 3 }
Joins an array with serial comma and serial conjunction, normally "and".
function p.invert(array)
An improvement on mw.text.listToText, which doesn't properly handle serial
checkType("invert", 1, array, "table")
commas.
Options:
local map = {}
- conj
for i, v in ipairs(array) do
Conjunction to use; defaults to "and".
map[v] = i
- italicizeConj
end
Italicize conjunction: for [[Module:Template:also]]
- dontTag
Don't tag the serial comma and serial "and". For error messages, in
which HTML cannot be used.
]=]
function export.serialCommaJoin(seq, options)
local check = _check("serialCommaJoin", "table")
check(1, seq)
check(2, options, true)
local length = #seq
return map
end
 
--[[
{ "a", "b", "c" } -> { ["a"] = true, ["b"] = true, ["c"] = true }
--]]
function p.listToSet(t)
checkType("listToSet", 1, t, "table")
if not options then
local set = {}
options = {}
for _, item in ipairs(t) do
set[item] = true
end
local conj
return set
if length > 1 then
end
conj = options.conj or "and"
 
if options.italicizeConj then
--[[
conj = "''" .. conj .. "''"
Recursive deep copy function.
end
Preserves identities of subtables.
]]
local function _deepCopy(orig, includeMetatable, already_seen)
-- Stores copies of tables indexed by the original table.
already_seen = already_seen or {}
local copy = already_seen[orig]
if copy ~= nil then
return copy
end
if type(orig)length == 'table'0 then
copyreturn = {}""
elseif length == 1 then
for orig_key, orig_value in pairs(orig) do
return seq[1] -- nothing to join
copy[deepcopy(orig_key, includeMetatable, already_seen)] = deepcopy(orig_value, includeMetatable, already_seen)
elseif length == 2 then
end
return seq[1] .. " " .. conj .. " " .. seq[2]
already_seen[orig] = copy
else
local comma = options.dontTag and "," or '<span class="serial-comma">,</span>'
if includeMetatable then
conj = options.dontTag and ' ' .. conj .. " " or '<span class="serial-and"> ' .. conj .. '</span> '
local mt = getmetatable(orig)
return table.concat(seq, ", ", 1, length - 1) ..
if mt ~= nil then
comma .. conj .. seq[length]
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
 
function p.deepCopy(orig, noMetatable, already_seen)
checkType("deepCopy", 3, already_seen, "table", true)
return _deepCopy(orig, not noMetatable, already_seen)
end
 
Line 409 ⟶ 603:
sparseConcat{ nil, b, c, d } => "bcd"
]]
function pexport.sparseConcat(t, sep, i, j)
local list = {}
local list_i = 0
for _, v in pexport.sparseIpairs(t) do
list_i = list_i + 1
list[list_i] = v
Line 422 ⟶ 616:
 
--[[
Values of numberic keys in array portion of table are reversed:
-- This returns the length of a table, or the first integer key n counting from
{ "a", "b", "c" } -> { "c", "b", "a" }
-- 1 such that t[n + 1] is nil. It is similar to the operator #, but may return
-- a different value when there are gaps in the array portion of the table.
-- Intended to be used on data loaded with mw.loadData. For other tables, use #.
-- Note: #frame.args in frame object always be set to 0, regardless of
-- the number of unnamed template parameters, so use this function for
-- frame.args.
--]]
function pexport.lengthreverse(t)
checkType("reverse", 1, t, "table")
local i = 1
while t[i] ~= nil do
local new_t = {}
i = i + 1
local new_t_i = 1
for i = #t, 1, -1 do
new_t[new_t_i] = t[i]
new_t_i = new_t_i + 1
end
return i - 1new_t
end
 
function pexport.inArrayreverseConcat(arrt, valueToFindsep, i, j)
return table.concat(export.reverse(t), sep, i, j)
checkType("inArray", 1, arr, "table")
end
 
-- { "a", "b", "c" } -> { a = 1, b = 2, c = 3 }
function export.invert(array)
checkType("invert", 1, array, "table")
local map = {}
-- if valueToFind is nil, error?
for i, v in ipairs(array) do
map[v] = i
end
return map
for _, v in ipairs(arr) do
end
if v == valueToFind then
 
return true
--[[
end
{ "a", "b", "c" } -> { ["a"] = true, ["b"] = true, ["c"] = true }
--]]
function export.listToSet(t)
checkType("listToSet", 1, t, "table")
local set = {}
for _, item in ipairs(t) do
set[item] = true
end
return set
end
 
--[[
Returns true if all keys in the table are consecutive integers starting at 1.
--]]
function export.isArray(t)
checkType("isArray", 1, t, "table")
local i = 0
return false
for _ in pairs(t) do
i = i + 1
if t[i] == nil then
return false
end
end
return true
end
 
return pexport