Theme: iWiki Log in Register

Diff: Module:TableTools

Comparing revision #1 (2022-01-31 13:08:18) with revision #2 (2023-02-02 05:32:49).

OldNew
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--                                   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 not --
-- be called directly from #invoke.                                               --
-- be called directly from #invoke.                                               --
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local libraryUtil = require('libraryUtil')
local p = {}
local p = {}
-- Define often-used variables and functions.
-- Define often-used variables and functions.
local floor = math.floor
local floor = math.floor
local infinity = math.huge
local infinity = math.huge
local checkType = libraryUtil.checkType
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
local checkTypeMulti = libraryUtil.checkTypeMulti
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- isPositiveInteger
-- isPositiveInteger
--
--
-- This function returns true if the given value is a positive integer, and false
-- 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
-- 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
-- useful for determining whether a given table key is in the array part or the
-- 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 if
-- not. Although it doesn't operate on tables, it is included here as it is useful
-- 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
-- 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.
-- 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 type(v) == 'number' and v ~= v
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- shallowClone
-- shallowClone
--
--
-- This returns a clone of a table. The value returned is a new table, but all
-- 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
-- subtables and functions are shared. Metamethods are respected, but the returned
-- table will have no metatable of its own.
-- table will have no metatable of its own.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.shallowClone(t)
function p.shallowClone(t)
	checkType('shallowClone', 1, t, 'table')
	checkType('shallowClone', 1, t, 'table')
	local ret = {}
	local ret = {}
	for k, v in pairs(t) do
	for k, v in pairs(t) do
		ret[k] = v
		ret[k] = v
	end
	end
	return ret
	return ret
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- removeDuplicates
-- removeDuplicates
--
--
-- This removes duplicate values from an array. Non-positive-integer keys are
-- This removes duplicate values from an array. Non-positive-integer keys are
-- ignored. The earliest value is kept, and all subsequent duplicate values are
-- ignored. The earliest value is kept, and all subsequent duplicate values are
-- removed, but otherwise the array order is unchanged.
-- removed, but otherwise the array order is unchanged.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.removeDuplicates(arr)
function p.removeDuplicates(arr)
	checkType('removeDuplicates', 1, arr, 'table')
	checkType('removeDuplicates', 1, arr, 'table')
	local isNan = p.isNan
	local isNan = p.isNan
	local ret, exists = {}, {}
	local ret, exists = {}, {}
	for _, v in ipairs(arr) do
	for _, v in ipairs(arr) do
		if isNan(v) then
		if isNan(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
		else
		else
			if not exists[v] then
			if not exists[v] then
				ret[#ret + 1] = v
				ret[#ret + 1] = v
				exists[v] = true
				exists[v] = true
			end
			end
		end
		end
	end
	end
	return ret
	return ret
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- numKeys
-- numKeys
--
--
-- This takes a table and returns an array containing the numbers of any numerical
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in numerical order.
-- keys that have non-nil values, sorted in numerical order.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.numKeys(t)
function p.numKeys(t)
	checkType('numKeys', 1, t, 'table')
	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 in pairs(t) do
		if isPositiveInteger(k) then
		if isPositiveInteger(k) then
			nums[#nums + 1] = k
			nums[#nums + 1] = k
		end
		end
	end
	end
	table.sort(nums)
	table.sort(nums)
	return nums
	return nums
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- affixNums
-- affixNums
--
--
-- This takes a table and returns an array containing the numbers of keys with the
-- This takes a table and returns an array containing the numbers of keys with the
-- specified prefix and suffix. For example, for the table
-- specified prefix and suffix. For example, for the table
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return
-- {1, 3, 6}.
-- {1, 3, 6}.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.affixNums(t, prefix, suffix)
function p.affixNums(t, prefix, suffix)
	checkType('affixNums', 1, t, 'table')
	checkType('affixNums', 1, t, 'table')
	checkType('affixNums', 2, prefix, 'string', true)
	checkType('affixNums', 2, prefix, 'string', true)
	checkType('affixNums', 3, suffix, 'string', true)
	checkType('affixNums', 3, suffix, 'string', true)
	local function cleanPattern(s)
	local function cleanPattern(s)
		-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
		-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
		return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
		return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
	end
	end
	prefix = prefix or ''
	prefix = prefix or ''
	suffix = suffix or ''
	suffix = suffix or ''
	prefix = cleanPattern(prefix)
	prefix = cleanPattern(prefix)
	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 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
				nums[#nums + 1] = tonumber(num)
				nums[#nums + 1] = tonumber(num)
			end
			end
		end
		end
	end
	end
	table.sort(nums)
	table.sort(nums)
	return nums
	return nums
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- numData
-- numData
--
--
-- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table
-- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table
-- of subtables in the format
-- of subtables in the format
-- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}.
-- {[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
-- 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
-- compress option compresses the table so that it can be iterated over with
-- ipairs.
-- 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)
	checkType('numData', 2, compress, 'boolean', true)
	local ret = {}
	local ret = {}
	for k, v in pairs(t) do
	for k, v in pairs(t) do
		local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
		local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
		if num then
		if num then
			num = tonumber(num)
			num = tonumber(num)
			local subtable = ret[num] or {}
			local subtable = ret[num] or {}
			if prefix == '' then
			if prefix == '' then
				-- Positional parameters match the blank string; put them at the start of the subtable instead.
				-- Positional parameters match the blank string; put them at the start of the subtable instead.
				prefix = 1
				prefix = 1
			end
			end
			subtable[prefix] = v
			subtable[prefix] = v
			ret[num] = subtable
			ret[num] = subtable
		else
		else
			local subtable = ret.other or {}
			local subtable = ret.other or {}
			subtable[k] = v
			subtable[k] = v
			ret.other = subtable
			ret.other = subtable
		end
		end
	end
	end
	if compress then
	if compress then
		local other = ret.other
		local other = ret.other
		ret = p.compressSparseArray(ret)
		ret = p.compressSparseArray(ret)
		ret.other = other
		ret.other = other
	end
	end
	return ret
	return ret
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- compressSparseArray
-- compressSparseArray
--
--
-- This takes an array with one or more nil values, and removes the nil values
-- 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
-- while preserving the order, so that the array can be safely traversed with
-- ipairs.
-- ipairs.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.compressSparseArray(t)
function p.compressSparseArray(t)
	checkType('compressSparseArray', 1, t, 'table')
	checkType('compressSparseArray', 1, t, 'table')
	local ret = {}
	local ret = {}
	local nums = p.numKeys(t)
	local nums = p.numKeys(t)
	for _, num in ipairs(nums) do
	for _, num in ipairs(nums) do
		ret[#ret + 1] = t[num]
		ret[#ret + 1] = t[num]
	end
	end
	return ret
	return ret
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- sparseIpairs
-- sparseIpairs
--
--
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
-- handle nil values.
-- handle nil values.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.sparseIpairs(t)
function p.sparseIpairs(t)
	checkType('sparseIpairs', 1, t, 'table')
	checkType('sparseIpairs', 1, t, 'table')
	local nums = p.numKeys(t)
	local nums = p.numKeys(t)
	local i = 0
	local i = 0
	local lim = #nums
	local lim = #nums
	return function ()
	return function ()
		i = i + 1
		i = i + 1
		if i <= lim then
		if i <= lim then
			local key = nums[i]
			local key = nums[i]
			return key, t[key]
			return key, t[key]
		else
		else
			return nil, nil
			return nil, nil
		end
		end
	end
	end
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- size
-- size
--
--
-- This returns the size of a key/value pair table. It will also work on arrays,
-- This returns the size of a key/value pair table. It will also work on arrays,
-- but for arrays it is more efficient to use the # operator.
-- but for arrays it is more efficient to use the # operator.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
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 _ in pairs(t) do
		i = i + 1
		i = i + 1
	end
	end
	return i
	return i
end
end
local function defaultKeySort(item1, item2)
local function defaultKeySort(item1, item2)
	-- "number" < "string", so numbers will be sorted before strings.
	-- "number" < "string", so numbers will be sorted before strings.
	local type1, type2 = type(item1), type(item2)
	local type1, type2 = type(item1), type(item2)
	if type1 ~= type2 then
	if type1 ~= type2 then
		return type1 < type2
		return type1 < type2
	elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then
	elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then
		return tostring(item1) < tostring(item2)
		return tostring(item1) < tostring(item2)
	else
	else
		return item1 < item2
		return item1 < item2
	end
	end
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- keysToList
-- keysToList
--
--
-- Returns an array of the keys in a table, sorted using either a default
-- Returns an array of the keys in a table, sorted using either a default
-- comparison function or a custom keySort function.
-- 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 arr = {}
	local arr = {}
	local index = 1
	local index = 1
	for k in pairs(t) do
	for k in pairs(t) do
		arr[index] = k
		arr[index] = k
		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(arr, keySort)
	end
	end
	return arr
	return arr
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- sortedPairs
-- sortedPairs
--
--
-- Iterates through a table, with the keys sorted using the keysToList function.
-- Iterates through a table, with the keys sorted using the keysToList function.
-- If there are only numerical keys, sparseIpairs is probably more efficient.
-- 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 arr = 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 = arr[i]
		if key ~= nil then
		if key ~= nil then
			return key, t[key]
			return key, t[key]
		else
		else
			return nil, nil
			return nil, nil
		end
		end
	end
	end
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- isArray
-- isArray
--
--
-- Returns true if the given value is a table and all keys are consecutive
-- Returns true if the given value is a table and all keys are consecutive
-- integers starting at 1.
-- integers starting at 1.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.isArray(v)
function p.isArray(v)
	if type(v) ~= 'table' then
	if type(v) ~= 'table' then
		return false
		return false
	end
	end
	local i = 0
	local i = 0
	for _ in pairs(v) do
	for _ in pairs(v) do
		i = i + 1
		i = i + 1
		if v[i] == nil then
		if v[i] == nil then
			return false
			return false
		end
		end
	end
	end
	return true
	return true
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- isArrayLike
-- isArrayLike
--
--
-- Returns true if the given value is iterable and all keys are consecutive
-- Returns true if the given value is iterable and all keys are consecutive
-- integers starting at 1.
-- integers starting at 1.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.isArrayLike(v)
function p.isArrayLike(v)
	if not pcall(pairs, v) then
	if not pcall(pairs, v) then
		return false
		return false
	end
	end
	local i = 0
	local i = 0
	for _ in pairs(v) do
	for _ in pairs(v) do
		i = i + 1
		i = i + 1
		if v[i] == nil then
		if v[i] == nil then
			return false
			return false
		end
		end
	end
	end
	return true
	return true
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- invert
-- invert
--
--
-- Transposes the keys and values in an array. For example, {"a", "b", "c"} ->
-- 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
-- {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.
-- the index of the last duplicate) and NaN values are ignored.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.invert(arr)
function p.invert(arr)
	checkType("invert", 1, arr, "table")
	checkType("invert", 1, arr, "table")
	local isNan = p.isNan
	local isNan = p.isNan
	local map = {}
	local map = {}
	for i, v in ipairs(arr) do
	for i, v in ipairs(arr) do
		if not isNan(v) then
		if not isNan(v) then
			map[v] = i
			map[v] = i
		end
		end
	end
	end
	return map
	return map
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- listToSet
-- listToSet
--
--
-- Creates a set from the array part of the table. Indexing the set by any of the
-- 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"} ->
-- 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
-- {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).
-- never equal to any value (including other NaNs or even themselves).
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.listToSet(arr)
function p.listToSet(arr)
	checkType("listToSet", 1, arr, "table")
	checkType("listToSet", 1, arr, "table")
	local isNan = p.isNan
	local isNan = p.isNan
	local set = {}
	local set = {}
	for _, v in ipairs(arr) do
	for _, v in ipairs(arr) do
		if not isNan(v) then
		if not isNan(v) then
			set[v] = true
			set[v] = true
		end
		end
	end
	end
	return set
	return set
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- deepCopy
-- deepCopy
--
--
-- Recursive deep copy function. Preserves identities of subtables.
-- 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
			end
			end
		end
		end
	else -- number, string, boolean, etc
	else -- number, string, boolean, etc
		copy = orig
		copy = orig
	end
	end
	return copy
	return copy
end
end
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
-- sparseConcat
--
--
-- Concatenates all values in the table that are indexed by a number, in order.
-- Concatenates all values in the table that are indexed by a number, in order.
-- sparseConcat{a, nil, c, d}  =>  "acd"
-- sparseConcat{a, nil, c, d}  =>  "acd"
-- sparseConcat{nil, b, c, d}  =>  "bcd"
-- sparseConcat{nil, b, c, d}  =>  "bcd"
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.sparseConcat(t, sep, i, j)
function p.sparseConcat(t, sep, i, j)
	local arr = {}
	local arr = {}
	local arr_i = 0
	local arr_i = 0
	for _, v in p.sparseIpairs(t) do
	for _, v in p.sparseIpairs(t) do
		arr_i = arr_i + 1
		arr_i = arr_i + 1
		arr[arr_i] = v
		arr[arr_i] = v
	end
	end
	return table.concat(arr, sep, i, j)
	return table.concat(arr, sep, i, j)
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- length
-- length
--
--
-- Finds the length of an array, or of a quasi-array with keys such as "data1",
-- 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
-- "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
-- 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
-- portion of the table. Intended to be used on data loaded with mw.loadData. For
-- other tables, use #.
-- other tables, use #.
-- Note: #frame.args in frame object always be set to 0, regardless of  the number
-- 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.
-- 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]] which is
	-- only needed by this one function doesn't get millions of transclusions
	-- 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
			key = prefix .. tostring(i)
			key = prefix .. tostring(i)
		else
		else
			key = i
			key = i
		end
		end
		return t[key] ~= nil
		return t[key] ~= nil
	end) or 0
	end) or 0
end
end
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- inArray
-- inArray
--
--
-- Returns true if valueToFind is a member of the array, and false otherwise.
-- Returns true if valueToFind is a member of the array, and false otherwise.
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
function p.inArray(arr, valueToFind)
function p.inArray(arr, valueToFind)
	checkType("inArray", 1, arr, "table")
	checkType("inArray", 1, arr, "table")
	-- if valueToFind is nil, error?
	-- if valueToFind is nil, error?
	for _, v in ipairs(arr) do
	for _, v in ipairs(arr) do
		if v == valueToFind then
		if v == valueToFind then
			return true
			return true
		end
		end
	end
	end
	return false
	return false
end
end
return p
return p