Module:TableTools:修订间差异

本页使用了标题或全文手工转换,现处于中国大陆简体模式
来自有兽档案馆
(更新自enwiki)
qiuwenbaike>WaitSpring
(已保护“Module:TableTools”([编辑=仅允许模板编辑员](无限期)[移动=仅允许模板编辑员](无限期)))
第1行: 第1行:
--[[
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- TableTools --
-- TableTools --
-- --
-- --
-- This module includes a number of functions for dealing with Lua tables. --
-- 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 --
-- It is a meta-module, meant to be called from other Lua modules, and should --
-- be called directly from #invoke. --
-- not be called directly from #invoke. --
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]


local libraryUtil = require('libraryUtil')
local libraryUtil = require('libraryUtil')
第17行: 第19行:
local checkTypeMulti = libraryUtil.checkTypeMulti
local checkTypeMulti = libraryUtil.checkTypeMulti


--[[
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- isPositiveInteger
-- isPositiveInteger
第25行: 第28行:
-- hash part of a table.
-- hash part of a table.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]
function p.isPositiveInteger(v)
function p.isPositiveInteger(v)
return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
end
end


--[[
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- isNan
-- isNan
--
--
-- This function returns true if the given number is a NaN value, and false if
-- This function returns true if the given number is a NaN value, and false
-- not. Although it doesn't operate on tables, it is included here as it is useful
-- if not. Although it doesn't operate on tables, it is included here as it is
-- for determining whether a value can be a valid table key. Lua will generate an
-- useful for determining whether a value can be a valid table key. Lua will
-- error if a NaN is used as a table key.
-- generate an error if a NaN is used as a table key.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]
function p.isNan(v)
function p.isNan(v)
return type(v) == 'number' and v ~= v
return not rawequal(v,v)
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)
function p.shallowClone(t)
checkType('shallowClone', 1, t, 'table')
local ret = {}
local ret = {}
for k, v in pairs(t) do
for k, v in pairs(t) do
第57行: 第56行:
end
end


function p.shallowCloneArray(t)
------------------------------------------------------------------------------------
local ret = {}
-- removeDuplicates
for k, v in ipairs(t) do
--
ret[k] = v
-- This removes duplicate values from an array. Non-positive-integer keys are
end
-- ignored. The earliest value is kept, and all subsequent duplicate values are
return ret
-- removed, but otherwise the array order is unchanged.
end
------------------------------------------------------------------------------------

function p.removeDuplicates(arr)
-- 浅复制一个表,但是忽略元表。
checkType('removeDuplicates', 1, arr, 'table')
function p.rawShallowClone(t)
local isNan = p.isNan
local ret
for _, v in next, t do
ret[t] = v
end
return ret
end

-- 往数组追加另一个数组的值。
function p.appendAll(t, another)
for _, v in ipairs(another) do
t[#t + 1] = v
end
end

-- 从数组中移除重复值。
function p.removeDuplicates(t)
local ret, exists = {}, {}
local ret, exists = {}, {}
for _, v in ipairs(arr) do
for i, v in ipairs(t) do
if isNan(v) then
if not rawequal(v, v) then
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
ret[#ret + 1] = v
ret[#ret + 1] = v
第77行: 第92行:
exists[v] = true
exists[v] = true
end
end
end
end
end
end
return ret
return ret
end
end


-- 返回有非nil值的数字键的表。
------------------------------------------------------------------------------------
-- 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)
function p.numKeys(t)
checkType('numKeys', 1, t, 'table')
local isPositiveInteger = p.isPositiveInteger
local isPositiveInteger = p.isPositiveInteger
local nums = {}
local nums = {}
for k in pairs(t) do
for k, v in pairs(t) do
if isPositiveInteger(k) then
if isPositiveInteger(k) then
nums[#nums + 1] = k
nums[#nums + 1] = k
第101行: 第110行:
end
end


-- 返回含有所有数字键的表,包括小数、负数。
------------------------------------------------------------------------------------
function p.allNumKeys(t)
-- affixNums
local nums = {}
--
for k, v in pairs(t) do
-- This takes a table and returns an array containing the numbers of keys with the
if type(k)=='number' then
-- specified prefix and suffix. For example, for the table
nums[#nums + 1] = k
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return
end
-- {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.
return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
end
end
table.sort(nums)
return nums
end


local function cleanPattern(s)
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
end

-- 搜索指定前后缀的整数键。
function p.affixNums(t, prefix, suffix)
prefix = prefix or ''
prefix = prefix or ''
suffix = suffix or ''
suffix = suffix or ''
第124行: 第134行:
suffix = cleanPattern(suffix)
suffix = cleanPattern(suffix)
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'

local nums = {}
local nums = {}
for k in pairs(t) do
for k, v in pairs(t) do
if type(k) == 'string' then
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
local num = mw.ustring.match(k, pattern)
if num then
if num then
第138行: 第147行:
end
end


--搜索指定前后缀的所有数字键,不限于整数。
------------------------------------------------------------------------------------
function p.allAffixNums(t, prefix, suffix)
-- numData
prefix = cleanPattern(prefix or '')
--
suffix = cleanPattern(suffix or '')
-- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table
local pattern = string.format('^%s(.-)%s$', prefix, suffix)
-- of subtables in the format
local nums = {}
-- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}.
for k, v in pairs(t) do
-- Keys that don't end with an integer are stored in a subtable named "other". The
if type(k) == 'string' then
-- compress option compresses the table so that it can be iterated over with
nums[#nums + 1] = tonumber(mw.ustring.match(k, pattern))
-- ipairs.
end
------------------------------------------------------------------------------------
end
table.sort(nums)
end

-- 将有("foo1", "bar1", "foo2", "baz2")这样键的表转换成
-- { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }
-- compress函数用来压实表以便用ipairs迭代。
function p.numData(t, compress)
function p.numData(t, compress)
checkType('numData', 1, t, 'table')
checkType('numData', 1, t, 'table')
checkType('numData', 2, compress, 'boolean', true)
local ret = {}
local ret = {}
for k, v in pairs(t) do
for k, v in pairs(t) do
第177行: 第192行:
end
end


-- 将像{a1='a1',b1='b1',a2='a2'}这样的表转换成
------------------------------------------------------------------------------------
-- {a = {'a1', 'a2'}, b = {'b1'}}
-- compressSparseArray
-- 注意可能是稀疏数组。
--
-- pattern是正则表达式,匹配前缀和数字。
-- This takes an array with one or more nil values, and removes the nil values
function p.allPrefixedNumKeys(t, pattern)
-- while preserving the order, so that the array can be safely traversed with
local ret = {}
-- ipairs.
for k, v in pairs(t) do
------------------------------------------------------------------------------------
local prefix, num = mw.ustring.match(tostring(k), pattern or '^([^0-9]*)([1-9][0-9]*)$')
num = tonumber(num)
if prefix and num then
local subtable = ret[prefix] or {}
subtable[num] = v
ret[prefix] = subtable
end
end
return ret
end

-- 将稀疏数组压实,移除nil值。
function p.compressSparseArray(t)
function p.compressSparseArray(t)
checkType('compressSparseArray', 1, t, 'table')
checkType('compressSparseArray', 1, t, 'table')
第194行: 第222行:
end
end


-- 将所有键为数字的字段转化成按数字大小排序的数组。
------------------------------------------------------------------------------------
function p.compressExtendedSparseArray(t)
-- sparseIpairs
checkType('compressExtendedSparseArray', 1, t, 'table')
--
local ret = {}
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
local nums = p.allNumKeys(t)
-- handle nil values.
for _, num in ipairs(nums) do
------------------------------------------------------------------------------------
ret[#ret + 1] = t[num]
end
return ret
end

function p.sparseIpairs(t)
function p.sparseIpairs(t)
checkType('sparseIpairs', 1, t, 'table')
checkType('sparseIpairs', 1, t, 'table')
第216行: 第249行:
end
end


function p.extendedSparseIpairs(t)
------------------------------------------------------------------------------------
checkType('extendedSparseIpairs', 1, t, 'table')
-- size
local nums = p.allNumKeys(t)
--
local i = 0
-- This returns the size of a key/value pair table. It will also work on arrays,
local lim = #nums
-- but for arrays it is more efficient to use the # operator.
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 p.size(t)
function p.size(t)
checkType('size', 1, t, 'table')
checkType('size', 1, t, 'table')
local i = 0
local i = 0
for _ in pairs(t) do
for k in pairs(t) do
i = i + 1
i = i + 1
end
end
return i
return i
end
end

-- 返回表中字段的数量,但是忽略元表。
function p.rawSize(t)
checkType('rawSize', 1, t, 'table')
local i = 0
for k in next, t do
i = i + 1
end
return i
end



local function defaultKeySort(item1, item2)
local function defaultKeySort(item1, item2)
第236行: 第291行:
if type1 ~= type2 then
if type1 ~= type2 then
return type1 < type2
return type1 < type2
elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then
else -- This will fail with table, boolean, function.
return tostring(item1) < tostring(item2)
else
return item1 < item2
return item1 < item2
end
end
end
end

------------------------------------------------------------------------------------
-- 返回表中的键的列表,并使用默认比较函数或者自定义keySort函数比较。
-- keysToList
--
-- Returns an array of the keys in a table, sorted using either a default
-- comparison function or a custom keySort function.
------------------------------------------------------------------------------------
function p.keysToList(t, keySort, checked)
function p.keysToList(t, keySort, checked)
if not checked then
if not checked then
checkType('keysToList', 1, t, 'table')
checkType('keysToList', 1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'})
checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
end
end
local list = {}
local index = 1
for key, value in pairs(t) do
list[index] = key
index = index + 1
end
if keySort ~= false then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
table.sort(list, keySort)
end
return list
end


-- 返回表中的键的列表,并使用默认比较函数或者自定义keySort函数比较,但是忽略元表。
local arr = {}
function p.rawKeysToList(t, keySort, checked)
if not checked then
checkType('keysToList', 1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
end
local list = {}
local index = 1
local index = 1
for k in pairs(t) do
for key, value in next, t do
arr[index] = k
list[index] = key
index = index + 1
index = index + 1
end
end

if keySort ~= false then
if keySort ~= false then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
keySort = type(keySort) == 'function' and keySort or defaultKeySort
table.sort(arr, keySort)
table.sort(list, keySort)
end
end

return arr
return list
end
end


-- 排序后迭代。
------------------------------------------------------------------------------------
-- sortedPairs
--
-- Iterates through a table, with the keys sorted using the keysToList function.
-- If there are only numerical keys, sparseIpairs is probably more efficient.
------------------------------------------------------------------------------------
function p.sortedPairs(t, keySort)
function p.sortedPairs(t, keySort)
checkType('sortedPairs', 1, t, 'table')
checkType('sortedPairs', 1, t, 'table')
checkType('sortedPairs', 2, keySort, 'function', true)
checkType('sortedPairs', 2, keySort, 'function', true)

local arr = p.keysToList(t, keySort, true)
local list = p.keysToList(t, keySort, true)

local i = 0
local i = 0
return function ()
return function()
i = i + 1
i = i + 1
local key = arr[i]
local key = list[i]
if key ~= nil then
if key ~= nil then
return key, t[key]
return key, t[key]
第293行: 第361行:
end
end


-- 排序后迭代,但是忽略元表。
------------------------------------------------------------------------------------
function p.rawSortedPairs(t, keySort)
-- isArray
checkType('sortedPairs', 1, t, 'table')
--
checkType('sortedPairs', 2, keySort, 'function', true)
-- Returns true if the given value is a table and all keys are consecutive
-- integers starting at 1.
local list = p.rawKeysToList(t, keySort, true)
------------------------------------------------------------------------------------
function p.isArray(v)
if type(v) ~= 'table' then
return false
end
local i = 0
local i = 0
return function()
for _ in pairs(v) do
i = i + 1
i = i + 1
local key = list[i]
if v[i] == nil then
if key ~= nil then
return false
return key, t[key]
else
return nil, nil
end
end
end
end
return true
end
end


-- 判断一个表是否为严格的数组。
------------------------------------------------------------------------------------
function p.isArray(t)
-- isArrayLike
checkType("isArray", 1, t, "table")
--
-- Returns true if the given value is iterable and all keys are consecutive
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArrayLike(v)
if not pcall(pairs, v) then
return false
end
local i = 0
local i = 0
for _ in pairs(v) do
for k, v in pairs(t) do
i = i + 1
i = i + 1
if v[i] == nil then
if t[i] == nil then
return false
return false
end
end
第333行: 第394行:
end
end


-- { "a", "b", "c" } -> { a = 1, b = 2, c = 3 }
------------------------------------------------------------------------------------
-- invert
function p.invert(array)
checkType("invert", 1, array, "table")
--
-- Transposes the keys and values in an array. 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(arr)
checkType("invert", 1, arr, "table")
local isNan = p.isNan
local map = {}
local map = {}
for i, v in ipairs(arr) do
for i, v in ipairs(array) do
map[v] = i
if not isNan(v) then
map[v] = i
end
end
end

return map
return map
end
end


--[[
------------------------------------------------------------------------------------
{ "a", "b", "c" } -> { ["a"] = true, ["b"] = true, ["c"] = true }
-- listToSet
--
--]]
function p.listToSet(t)
-- Creates a set from the array part of the table. Indexing the set by any of the
checkType("listToSet", 1, t, "table")
-- values of the array returns true. 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(arr)
checkType("listToSet", 1, arr, "table")
local isNan = p.isNan
local set = {}
local set = {}
for _, v in ipairs(arr) do
for _, item in ipairs(t) do
set[item] = true
if not isNan(v) then
set[v] = true
end
end
end

return set
return set
end
end


-- 递归深度拷贝,保护标识和子表。
------------------------------------------------------------------------------------
-- deepCopy
--
-- Recursive deep copy function. Preserves identities of subtables.
------------------------------------------------------------------------------------
local function _deepCopy(orig, includeMetatable, already_seen)
local function _deepCopy(orig, includeMetatable, already_seen)
-- Stores copies of tables indexed by the original table.
-- Stores copies of tables indexed by the original table.
already_seen = already_seen or {}
already_seen = already_seen or {}

local copy = already_seen[orig]
local copy = already_seen[orig]
if copy ~= nil then
if copy ~= nil then
return copy
return copy
end
end

if type(orig) == 'table' then
if type(orig) == 'table' then
copy = {}
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)
copy[deepcopy(orig_key, includeMetatable, already_seen)] = deepcopy(orig_value, includeMetatable, already_seen)
end
end
already_seen[orig] = copy
already_seen[orig] = copy

if includeMetatable then
if includeMetatable then
local mt = getmetatable(orig)
local mt = getmetatable(orig)
if mt ~= nil then
if mt ~= nil then
local mt_copy = _deepCopy(mt, includeMetatable, already_seen)
local mt_copy = deepcopy(mt, includeMetatable, already_seen)
setmetatable(copy, mt_copy)
setmetatable(copy, mt_copy)
already_seen[mt] = mt_copy
already_seen[mt] = mt_copy
第411行: 第453行:
function p.deepCopy(orig, noMetatable, already_seen)
function p.deepCopy(orig, noMetatable, already_seen)
checkType("deepCopy", 3, already_seen, "table", true)
checkType("deepCopy", 3, already_seen, "table", true)
return _deepCopy(orig, not noMetatable, already_seen)
return _deepCopy(orig, not noMetatable, already_seen)
end
end


-- sparseConcat{ a, nil, c, d } => "acd"
------------------------------------------------------------------------------------
-- sparseConcat
-- sparseConcat{ nil, b, c, d } => "bcd"
--
-- 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)
function p.sparseConcat(t, sep, i, j)
local arr = {}
local list = {}

local arr_i = 0
local list_i = 0
for _, v in p.sparseIpairs(t) do
for _, v in p.sparseIpairs(t) do
arr_i = arr_i + 1
list_i = list_i + 1
arr[arr_i] = v
list[list_i] = v
end
end
return table.concat(list, sep, i, j)
end


return table.concat(arr, sep, i, j)
function p.inArray(arr, valueToFind)
checkType("inArray", 1, arr, "table")
for _, v in ipairs(arr) do
if v == valueToFind then
return true
end
end
return false
end

function p.visit(t,notNum)
local keys = {}
for k, v in pairs(t) do
if type(k)=="number" then
table.insert(keys,k)
else
if notNum then
notNum(k,v,keys,t)
end
end
end
local i = 0
return function()
i = i+1
return keys[i],key[i] and t[keys[i]]
end
end
end


------------------------------------------------------------------------------------
-- length
--
-- Finds the length of an array, or of a quasi-array with keys such as "data1",
-- "data2", etc., using an exponential search algorithm. 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 p.length(t, prefix)
function p.length(t, prefix)
-- requiring module inline so that [[Module:Exponential search]] which is
-- requiring module inline so that [[Module:Exponential search]]
-- only needed by this one function doesn't get millions of transclusions
-- which is only needed by this one function
-- doesn't get millions of transclusions
local expSearch = require("Module:Exponential search")
local expSearch = require("Module:Exponential search")
checkType('length', 1, t, 'table')
checkType('length', 1, t, 'table')
checkType('length', 2, prefix, 'string', true)
checkType('length', 2, prefix, 'string', true)
return expSearch(function (i)
return expSearch(function(i)
local key
local key
if prefix then
if prefix then
第460行: 第518行:
end) or 0
end) or 0
end
end

------------------------------------------------------------------------------------
-- inArray
--
-- Returns true if valueToFind is a member of the array, and false otherwise.
------------------------------------------------------------------------------------
function p.inArray(arr, valueToFind)
checkType("inArray", 1, arr, "table")
-- if valueToFind is nil, error?

for _, v in ipairs(arr) do
if v == valueToFind then
return true
end
end
return false
end

return p
return p

2023年2月4日 (六) 18:41的版本

本模块包含一系列用于处理Lua表的函数。这是个元模块,用于其他Lua模块调用,而不应该由#invoke直接调用。

加载模块

如需加载任何函数,首先需要加载模块。

local TableTools = require('Module:TableTools')

isPositiveInteger

TableTools.isPositiveInteger(value)

如果value是正整数,返回true,否则返回false。虽然此函数不是应用于表的,但这里仍收录了此函数以决定一个给定的键是否在表的数组部分或哈希部分。

isNan

TableTools.isNan(value)

如果value是NaN值,返回true,否则返回false。虽然此函数不是应用于表的,但这里仍收录了此函数以决定一个值能否作为有效的键。(将NaN作为键会报错。)

shallowClone

TableTools.shallowClone(t)

返回表格的副本。返回的值是一个新表,但是所有的子表和函数都会共享。不会绕过元表,但返回的表自身不会带有元表。如需在创建新表时不共享子表格且要复制元表,可以使用mw.clone。

shallowCloneArray

TableTools.shallowCloneArray(t)

返回数组的副本。返回的值是一个新的数组类似于shallowClone,但是只会复制数组部分。

rawShallowClone

TableTools.rawShallowClone(t)

类似于TableTools.shallowClone,但是迭代时会绕过__pairs元表。

appendAll

TableTools.appendAll(t, anotherArray)

在数组t的末尾添加数组anotherArray的所有内容。会修改t,不返回值。

removeDuplicates

TableTools.removeDuplicates(t)

从数组中返回重复值。此函数仅用于标准的数组,所有非正整数键的一律忽略,第一个nil后的值也会忽略。(对于包含nil的数组,可以先使用compressSparseArray。)此函数尝试保护数组的顺序:有重复值时,仅保留最早出现的那个。例如,对于表{5, 4, 4, 3, 4, 2, 2, 1},removeDuplicates返回{5, 4, 3, 2, 1}

numKeys

TableTools.numKeys(t)

接收表t并以数字顺序返回拥有非nil值的正整数键的数量。例如,对于{'foo', nil, 'bar', 'baz', a = 'b'},numKeys返回{1, 3, 4}

allNumKeys

TableTools.allNumKeys(t)

类似于numKeys,但是不限于正整数。

affixNums

TableTools.affixNums(t, prefix, suffix)

接收表t并返回包含含有可选前缀prefix和可选后缀suffix的键的数量的数组。例如,对于表{a1 = 'foo', a3 = 'bar', a6 = 'baz'}和前缀'a',affixNums返回{1, 3, 6}prefixsuffix的值均按字面解释(不使用正则表达式)。

allAffixNums

TableTools.allAffixNums(t, prefix, suffix)

类似于affixNums,但是不限于正整数。

numData

TableTools.numData(t, compress)

假如一个表有像foo1、bar1、foo2、baz2这样的键,返回如下格式的子表的表{ [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }。不以整数结尾的键存储在命名为other的子表中。compress选项会压缩表以便通过ipairs迭代。

allPrefixedNumKeys

TableTools.allPrefixedNumKeys(t, pattern)

假设有{a1 = 'a1', a2 = 'a2', a3 = 'a3', b1 = 'b1', b2 = 'b2'}这样的表,返回{a = {'a1', 'a2', 'a3'}, b = {'b1', 'b2'} }这样的表。非以数字结尾的表会被忽略,且该表的子表可能是稀疏数组。

pattern用于指定正则表达式匹配。例如,若pattern'^(category)(.+)$'只会匹配以“category”开头,任意数字(可以是小数、负数)结尾的部分。这个正则表达式需要捕获两个值,第一个值是前缀,即返回的表的键,第二个值是可能是数字的部分,应当有可能用tonumber转化为数字。

compressSparseArray

TableTools.compressSparseArray(t)

接收一个带有一个或更多nil值的数组t,移除所有的nil值,不影响其他值的顺序,以便于安全地通过ipairs遍历。所有非正整数的键都会移除。例如,对于表{1, nil, foo = 'bar', 3, 2},compressSparseArray返回{1, 3, 2}

compressExtendedSparseArray

TableTools.compressExtendedSparseArray(t)

类似于compressSparseArray,但是不会移除非正整数键和小数键。

sparseIpairs

TableTools.sparseIpairs(t)

这是用于遍历稀疏数组t的迭代器函数。类似于ipairs,但会一直迭代直到最高的数字键,而ipairs遇到第一个nil就可能会终止。忽略所有的非正整数键。

sparseIpairs常用于通用的for循环。

for i, v in TableTools.sparseIpairs(t) do
   -- 代码
end

注意sparseIpairs会在实现时使用pairs。尽管一些键看上去被忽略,但实际上运行时也会遍历到。

extendedSparseIpairs

TableTools.extendedSparseIpairs(t)

类似于sparseIpairs,但是不会忽略非正整数键和小数键。

size

TableTools.size(t)

寻找“键/值对”表的大小。例如,对于{foo = 'foo', bar = 'bar'},size返回2。函数也可以用于数组,但是对于数组,使用#操作符更高效。注意,为了查找表的大小,函数会使用pairs函数以遍历所有的表键。

rawSize

TableTools.rawSize(t)

类似于size,但是会忽略__pairs元方法。

keysToList

TableTools.keysToList(t, keySort)

返回表的键的列表,并按默认比较函数或者自定义keySort函数排序,与table.sortcomp函数类似。

rawKeysToList

TableTools.rawKeysToList(t, keySort)

类似于keysToList,但是会忽略__pairs元方法。

sortedPairs

TableTools.sortedPairs(t, keySort)

迭代表,并使用keysToList函数来排序键。如果只有数字键,sparseIpairs可能更高效。

该函数不能作为表的__pairs元方法,否则会出现堆栈溢出。

rawSortedPairs

TableTools.rawSortedPairs(t, keySort)

类似于rawSortedPairs,但是会忽略__pairs元方法,可以作为表的__pairs元方法。

isArray

TableTools.isArray(t)

如果表中的所有键都是以1开始的接连不断的整数,返回true。

listToSet

TableTools.listToSet(arr)

从表arr的数组部分创建一个集(set)。用arr的任何值来索引集都会返回true

local set = TableTools.listToSet { "a", "b", "c" }
assert(set["a"] == true)

invert

TableTools.invert(t)

交换数组中的键和值。例如,invert{ "a", "b", "c" }产生{ a = 1, b = 2, c = 3 }

deepCopy

TableTools.deepCopy(orig, noMetatable, alreadySeen)

返回表orig的副本,类似于mw.clone,所有非函数的值都会复制,并保留表的唯一性。如果noMetatabletrue,则不会复制元表(如有)。可以复制由mw.loadData加载的表。

mw.clone类似,但mw.clone不能够复制由mw.loadData加载的表,也不允许拒绝复制元表。

sparseConcat

TableTools.sparseConcat(t, sep)

按顺序连接被正整数索引的表的所有值。

inArray

TableTools.inArray(arr, valueToFind)

如果valueToFind是数组arr的成员,返回true,否则返回false

上述文档内容嵌入自Module:TableTools/doc编辑 | 历史
编者可以在本模块的沙盒创建 | 镜像和测试样例创建页面进行实验。
请将模块自身所属的分类添加在文档中。本模块的子页面
--[[
------------------------------------------------------------------------------------
--                               TableTools                                       --
--                                                                                --
-- 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 be called directly from #invoke.                                           --
------------------------------------------------------------------------------------
--]]

local libraryUtil = require('libraryUtil')

local p = {}

-- Define often-used variables and functions.
local floor = math.floor
local infinity = math.huge
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti

--[[
------------------------------------------------------------------------------------
-- 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)
	return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
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)
	return not rawequal(v,v)
end

-- 浅复制一个表。
function p.shallowClone(t)
	local ret = {}
	for k, v in pairs(t) do
		ret[k] = v
	end
	return ret
end

function p.shallowCloneArray(t)
	local ret = {}
	for k, v in ipairs(t) do
		ret[k] = v
	end
	return ret
end

-- 浅复制一个表,但是忽略元表。
function p.rawShallowClone(t)
	local ret
	for _, v in next, t do
		ret[t] = v
	end
	return ret
end

-- 往数组追加另一个数组的值。
function p.appendAll(t, another)
	for _, v in ipairs(another) do
		t[#t + 1] = v
	end
end

-- 从数组中移除重复值。
function p.removeDuplicates(t)
	local ret, exists = {}, {}
	for i, v in ipairs(t) do
		if not rawequal(v, 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

-- 返回有非nil值的数字键的表。
function p.numKeys(t)
	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

-- 返回含有所有数字键的表,包括小数、负数。
function p.allNumKeys(t)
	local nums = {}
	for k, v in pairs(t) do
		if type(k)=='number' then
			nums[#nums + 1] = k
		end
	end
	table.sort(nums)
	return nums
end

local function cleanPattern(s)
	-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
	return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
end

-- 搜索指定前后缀的整数键。
function p.affixNums(t, prefix, suffix)
	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

--搜索指定前后缀的所有数字键,不限于整数。
function p.allAffixNums(t, prefix, suffix)
	prefix = cleanPattern(prefix or '')
	suffix = cleanPattern(suffix or '')
	local pattern = string.format('^%s(.-)%s$', prefix, suffix)
	local nums = {}
	for k, v in pairs(t) do
		if type(k) == 'string' then
			nums[#nums + 1] = tonumber(mw.ustring.match(k, pattern))
		end
	end
	table.sort(nums)
end

-- 将有("foo1", "bar1", "foo2", "baz2")这样键的表转换成
-- { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }
-- compress函数用来压实表以便用ipairs迭代。
function p.numData(t, compress)
	checkType('numData', 1, t, 'table')
	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

-- 将像{a1='a1',b1='b1',a2='a2'}这样的表转换成
-- {a = {'a1', 'a2'}, b = {'b1'}}
-- 注意可能是稀疏数组。
-- pattern是正则表达式,匹配前缀和数字。
function p.allPrefixedNumKeys(t, pattern)
	local ret = {}
	for k, v in pairs(t) do
		local prefix, num = mw.ustring.match(tostring(k), pattern or '^([^0-9]*)([1-9][0-9]*)$')
		num = tonumber(num)
		if prefix and num then
			local subtable = ret[prefix] or {}
			subtable[num] = v
			ret[prefix] = subtable
		end
	end
	return ret
end
		

-- 将稀疏数组压实,移除nil值。
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

-- 将所有键为数字的字段转化成按数字大小排序的数组。
function p.compressExtendedSparseArray(t)
	checkType('compressExtendedSparseArray', 1, t, 'table')
	local ret = {}
	local nums = p.allNumKeys(t)
	for _, num in ipairs(nums) do
		ret[#ret + 1] = t[num]
	end
	return ret
end

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 p.extendedSparseIpairs(t)
	checkType('extendedSparseIpairs', 1, t, 'table')
	local nums = p.allNumKeys(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 p.size(t)
	checkType('size', 1, t, 'table')
	local i = 0
	for k in pairs(t) do
		i = i + 1
	end
	return i
end

-- 返回表中字段的数量,但是忽略元表。
function p.rawSize(t)
	checkType('rawSize', 1, t, 'table')
	local i = 0
	for k in next, t do
		i = i + 1
	end
	return i
end


local function defaultKeySort(item1, item2)
	-- "number" < "string", so numbers will be sorted before strings.
	local type1, type2 = type(item1), type(item2)
	if type1 ~= type2 then
		return type1 < type2
	else -- This will fail with table, boolean, function.
		return item1 < item2
	end
end

-- 返回表中的键的列表,并使用默认比较函数或者自定义keySort函数比较。
function p.keysToList(t, keySort, checked)
	if not checked then
		checkType('keysToList', 1, t, 'table')
		checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
	end
	
	local list = {}
	local index = 1
	for key, value in pairs(t) do
		list[index] = key
		index = index + 1
	end
	
	if keySort ~= false then
		keySort = type(keySort) == 'function' and keySort or defaultKeySort
		
		table.sort(list, keySort)
	end
	
	return list
end

-- 返回表中的键的列表,并使用默认比较函数或者自定义keySort函数比较,但是忽略元表。
function p.rawKeysToList(t, keySort, checked)
	if not checked then
		checkType('keysToList', 1, t, 'table')
		checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
	end
	
	local list = {}
	local index = 1
	for key, value in next, t do
		list[index] = key
		index = index + 1
	end
	
	if keySort ~= false then
		keySort = type(keySort) == 'function' and keySort or defaultKeySort
		
		table.sort(list, keySort)
	end
	
	return list
end

-- 排序后迭代。
function p.sortedPairs(t, keySort)
	checkType('sortedPairs', 1, t, 'table')
	checkType('sortedPairs', 2, keySort, 'function', true)
	
	local list = p.keysToList(t, keySort, true)
	
	local i = 0
	return function()
		i = i + 1
		local key = list[i]
		if key ~= nil then
			return key, t[key]
		else
			return nil, nil
		end
	end
end

-- 排序后迭代,但是忽略元表。
function p.rawSortedPairs(t, keySort)
	checkType('sortedPairs', 1, t, 'table')
	checkType('sortedPairs', 2, keySort, 'function', true)
	
	local list = p.rawKeysToList(t, keySort, true)
	
	local i = 0
	return function()
		i = i + 1
		local key = list[i]
		if key ~= nil then
			return key, t[key]
		else
			return nil, nil
		end
	end
end

-- 判断一个表是否为严格的数组。
function p.isArray(t)
	checkType("isArray", 1, t, "table")
	
	local i = 0
	for k, v in pairs(t) do
		i = i + 1
		if t[i] == nil then
			return false
		end
	end
	return true
end

-- { "a", "b", "c" } -> { a = 1, b = 2, c = 3 }
function p.invert(array)
	checkType("invert", 1, array, "table")
	
	local map = {}
	for i, v in ipairs(array) do
		map[v] = i
	end
	
	return map
end

--[[
	{ "a", "b", "c" } -> { ["a"] = true, ["b"] = true, ["c"] = true }
--]]
function p.listToSet(t)
	checkType("listToSet", 1, t, "table")
	
	local set = {}
	for _, item in ipairs(t) do
		set[item] = true
	end
	
	return set
end

-- 递归深度拷贝,保护标识和子表。
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)
				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

-- sparseConcat{ a, nil, c, d }  =>  "acd"
-- sparseConcat{ nil, b, c, d }  =>  "bcd"
function p.sparseConcat(t, sep, i, j)
	local list = {}
	
	local list_i = 0
	for _, v in p.sparseIpairs(t) do
		list_i = list_i + 1
		list[list_i] = v
	end
	
	return table.concat(list, sep, i, j)
end

function p.inArray(arr, valueToFind)
	checkType("inArray", 1, arr, "table")
	
	for _, v in ipairs(arr) do
		if v == valueToFind then
			return true
		end
	end
	
	return false
end

function p.visit(t,notNum)
	local keys = {}
	for k, v in pairs(t) do
		if type(k)=="number" then
			table.insert(keys,k)
		else
			if notNum then
	    		notNum(k,v,keys,t)
			end
		end
	end
	local i = 0
	return function()
		i = i+1
		return keys[i],key[i] and t[keys[i]]
	end
end

function p.length(t, prefix)
	-- requiring module inline so that [[Module:Exponential search]]
	-- which is only needed by this one function
	-- 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
			key = prefix .. tostring(i)
		else
			key = i
		end
		return t[key] ~= nil
	end) or 0
end
return p