Módulo:Testes/Gkiyoshinishimoto/TableTools

Documentação do módulo[ver] [editar] [histórico] [purgar]

Este módulo inclui várias funções para lidar com tabelas Lua. É um metamódulo, destinado a ser chamado a partir de outros módulos Lua e não deve ser chamado diretamente a partir de "#invoke".

Carregando o módulo

editar

Para usar qualquer uma das funções, primeiro é necessário carregar o módulo.

local TableTools = require('Módulo:Testes/Gkiyoshinishimoto/TableTools')

isPositiveInteger

editar
TableTools.isPositiveInteger(value)

Esta função retorna true se value for um número inteiro positivo e false se não for. Embora não opere em tabelas, está incluída aqui porque é útil para determinar se uma determinada chave de tabela está na parte arranjo ou na parte hash de uma tabela.

TableTools.isNan(value)

Esta função retorna true se value for um valor NaN e false se não for. Embora não opere em tabelas, está incluída aqui porque é útil para determinar se um valor pode ser uma chave de tabela válida. (Lua gerará um erro se um valor NaN for usado como uma chave de tabela.)

shallowClone

editar
TableTools.shallowClone(t)

Esta função retorna um clone de uma tabela. O valor retornado é uma nova tabela, mas todas as subtabelas e funções são compartilhadas. Os metamétodos são respeitados, mas a tabela retornada não terá metatabela própria. Se você quiser criar uma nova tabela sem subtabelas compartilhadas e com metatabelas transferidas, você pode usar mw.clone. Se você deseja criar uma nova tabela sem subtabelas compartilhadas e sem metatabelas transferidas, use deepCopy com a opção noMetatable.

removeDuplicates

editar
TableTools.removeDuplicates(t)

Esta função remove valores duplicados de um arranjo. Esta função foi projetada para funcionar apenas com arranjos padrões: as chaves que não são números inteiros positivos são ignoradas, assim como todos os valores após o primeiro valor nil. (Para arranjos contendo valores nil, você pode usar compressSparseArray primeiro.) A função tenta preservar a ordem do arranjo: o primeiro valor que não for exclusivo (único) é mantido e todos os valores duplicados subsequentes são removidos. Por exemplo, para a tabela {5, 4, 4, 3, 4, 2, 2, 1} removeDuplicates retornará {5, 4, 3, 2, 1}.

numKeys

editar
TableTools.numKeys(t)

Esta função pega uma tabela t e retorna um arranjo contendo os números de quaisquer chaves inteiras positivas que tenham valores que não são nulos, classificados em ordem numérica. Por exemplo, para a tabela {'foo', nil, 'bar', 'baz', a = 'b'}, numKeys retornará {1, 3, 4}.

affixNums

editar
TableTools.affixNums(t, prefix, suffix)

Esta função pega uma tabela t e retorna um arranjo contendo os números de chaves com o prefixo opcional prefix e o sufixo opcional suffix. Por exemplo, para a tabela {a1 = 'foo', a3 = 'bar', a6 = 'baz'} e o prefixo 'a' , affixNums retornará {1, 3, 6}. Todos os caracteres em prefix e suffix são interpretados literalmente.

numData

editar
TableTools.numData(t, compress)

Dada uma tabela com chaves como "foo1", "bar1", "foo2" e "baz2", esta função retorna uma tabela de subtabelas no formato { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text' } }. As chaves que não terminam com um número inteiro são armazenadas em uma subtabela chamada "other". A opção "compress" comprime a tabela para que possa ser iterada com ipairs.

compressSparseArray

editar
TableTools.compressSparseArray(t)

Esta função pega um arranjo t com um ou mais valores nulos, e remove os valores nulos enquanto preserva a ordem, para que o arranjo possa ser percorrido com segurança com ipairs. Quaisquer chaves que não sejam números inteiros positivos são removidas. Por exemplo, para a tabela {1, nil, foo = 'bar', 3, 2}, compressSparseArray retornará {1, 3, 2}.

sparseIpairs

editar
TableTools.sparseIpairs(t)

Esta é uma função iteradora para percorrer um arranjo esparso t. É semelhante a ipairs, mas continuará a iterar até a chave numérica mais alta, enquanto ipairs pode parar após o primeiro valor nil. Quaisquer chaves que não sejam números inteiros positivos são ignoradas.

Normalmente, sparseIpairs é usada em um loop for genérico.

for i, v in TableTools.sparseIpairs(t) do
   -- bloco de código
end

Observe que sparseIpairs usa a função pairs em sua implementação. Embora algumas chaves da tabela pareçam ser ignoradas, todas as chaves da tabela são acessadas quando ela é executado.

TableTools.size(t)

Localiza o tamanho de uma tabela de pares chave/valor. Por exemplo, para a tabela {foo = 'foo', bar = 'bar'}, size retornará 2. A função também funcionará em arranjos, mas para arranjos é mais eficiente usar o operador #. Observe que para encontrar o tamanho da tabela, esta função usa a função pairs para percorrer todas as chaves da tabela.

keysToList

editar
TableTools.keysToList(t, keySort, checked)

Esta função retorna uma lista das chaves em uma tabela, classificadas usando uma função de comparação padrão ou uma função keySort personalizada, que segue as mesmas regras da função comp fornecida para table.sort. Se keySort for false, nenhuma classificação será feita. É necessário definir checked como true para pular a verificação interna de tipo.

sortedPairs

editar
TableTools.sortedPairs(t, keySort)

Esta função itera por meio de uma tabela, com as chaves classificadas usando a função keysToList. Se houver apenas chaves numéricas, sparseIpairs provavelmente será mais eficiente.

isArray

editar
TableTools.isArray(value)

Esta função retorna true se value for uma tabela e todas as chaves forem inteiros consecutivos começando em 1.

isArrayLike

editar
TableTools.isArrayLike(value)

Esta função retorna true se value for iterável e todas as chaves forem inteiros consecutivos começando em 1.

invert

editar
TableTools.invert(arr)

Esta função transpõe as chaves e valores em um arranjo. Por exemplo, invert{ "a", "b", "c" } resulta em { a=1, b=2, c=3 }.

listToSet

editar
TableTools.listToSet(arr)

Esta função cria um conjunto a partir da parte arranjo da tabela arr. A indexação do conjunto por qualquer um dos valores do arranjo retorna true. Por exemplo, listToSet{ "a", "b", "c" } resulta em { a=true, b=true, c=true }. Consulte também Módulo:Testes/Gkiyoshinishimoto/Lua set [en] para formas mais avançadas de criar um conjunto.

deepCopy

editar
TableTools.deepCopy(orig, noMetatable, alreadySeen)

Esta função cria uma cópia da tabela orig. Como em mw.clone, todos os valores que não são funções são duplicados e a identidade das tabelas é preservada. Se noMetatable for true, então a metatabela (se houver) não será copiada. Ela pode copiar tabelas carregadas com mw.loadData.

Similar à mw.clone, mas mw.clone não pode copiar tabelas carregadas com mw.loadData e não permite que metatabelas não sejam copiadas.

sparseConcat

editar
TableTools.sparseConcat(t, sep, i, j)

Esta função concatena todos os valores na tabela que são indexados por um número inteiro positivo, em ordem. Por exemplo, sparseConcat{ "a", nil, "c", "d" } resulta em "acd" e sparseConcat{ nil, "b ", "c", "d" } resulta em "bcd".

length

editar
TableTools.length(t, prefix)

Esta função localiza o comprimento de um arranjo ou de um quasi-arranjo com chaves com um prefix opcional como "data1", "data2", etc. Ela usa um algoritmo de pesquisa exponencial para encontrar o comprimento, de modo a usar o mínimo possível de pesquisas de tabela.

Este algoritmo é útil para arranjos que usam metatabelas (por exemplo, frame.args) e para quasi-arranjos. Para arranjos normais, basta usar o operador #, pois ele é implementado em C e será mais rápido.

inArray

editar
TableTools.inArray(arr, valueToFind)

Esta função retorna true se valueToFind for um membro do arranjo arr e false se não for.

------------------------------------------------------------------------------------
--                                   TableTools                                   --
--                                                                                --
-- Este módulo inclui várias funções para lidar com tabelas Lua.                  --
-- É um metamódulo, destinado a ser chamado a partir de outros módulos Lua, e     --
-- não deve ser chamado diretamente a partir de "#invoke".                        --
------------------------------------------------------------------------------------

local libraryUtil = require('libraryUtil')

local p = {}

-- Define variáveis e funções usadas com frequência.
local floor = math.floor
local infinity = math.huge
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti

------------------------------------------------------------------------------------
-- isPositiveInteger
--
-- Esta função retorna "true" se o valor fornecido for um número inteiro positivo e
-- "false" se não. Embora não opere em tabelas, ela é incluída aqui porque ela é
-- útil para determinar se uma determinada chave de tabela está na parte de arranjo
-- ('array') ou na parte de 'hash' de uma tabela.
------------------------------------------------------------------------------------
function p.isPositiveInteger(v)
	return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
end

------------------------------------------------------------------------------------
-- isNan
--
-- Essa função retorna "true" se o número fornecido for um valor de "NaN" e "false" 
-- se não. Embora não opere em tabelas, ela é incluída aqui porque ela é útil para
-- determinar se um valor pode ser uma chave de tabela válida. Lua irá gerar um
-- erro se um "NaN" for usado como uma chave de tabela.
------------------------------------------------------------------------------------
function p.isNan(v)
	return type(v) == 'number' and v ~= v
end

------------------------------------------------------------------------------------
-- shallowClone
--
-- Essa função retorna um clone de uma tabela. O valor retornado é uma nova tabela,  
-- mas todas as subtabelas e funções são compartilhadas. Os metamétodos são 
-- respeitados, mas a tabela retornada não terá metatabela própria.
------------------------------------------------------------------------------------
function p.shallowClone(t)
	checkType('shallowClone', 1, t, 'table')
	local ret = {}
	for k, v in pairs(t) do
		ret[k] = v
	end
	return ret
end

------------------------------------------------------------------------------------
-- removeDuplicates
--
-- Essa função remove os valores duplicados de um arranjo ('array'). As chaves de  
-- números inteiros que não são positivos são ignoradas. O valor mais antigo é 
-- mantido e todos os valores duplicados subsequentes são removidos, mas caso não,
-- a ordem do arranjo ('array') permanece inalterada.
------------------------------------------------------------------------------------
function p.removeDuplicates(arr)
	checkType('removeDuplicates', 1, arr, 'table')
	local isNan = p.isNan
	local ret, exists = {}, {}
	for _, v in ipairs(arr) do
		if isNan(v) then
			-- Os "NaN"s não podem ser chaves de tabela e também são únicos,
			-- portanto não precisamos verificar a existência.
			ret[#ret + 1] = v
		else
			if not exists[v] then
				ret[#ret + 1] = v
				exists[v] = true
			end
		end
	end
	return ret
end

------------------------------------------------------------------------------------
-- numKeys
--
-- Essa função pega uma tabela e retorna um arranjo ('array') contendo os números 
-- de quaisquer chaves numéricas que tenham valores que não são nulos ('nil'),
-- classificados em ordem numérica.
------------------------------------------------------------------------------------
function p.numKeys(t)
	checkType('numKeys', 1, t, 'table')
	local isPositiveInteger = p.isPositiveInteger
	local nums = {}
	for k in pairs(t) do
		if isPositiveInteger(k) then
			nums[#nums + 1] = k
		end
	end
	table.sort(nums)
	return nums
end

------------------------------------------------------------------------------------
-- affixNums
--
-- Essa função pega uma tabela e retorna um arranjo ('array') contendo os números 
-- de chaves com o prefixo e sufixo especificados. Por exemplo, para a tabela
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} e o prefixo "a", "affixNums" retornará
-- {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)
		-- Limpa um padrão ('pattern') para que os caracteres mágicos "()%.[]*+-?^$"
		-- sejam interpretados literalmente.
		return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
	end

	prefix = prefix or ''
	suffix = suffix or ''
	prefix = cleanPattern(prefix)
	suffix = cleanPattern(suffix)
	local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'

	local nums = {}
	for k in pairs(t) do
		if type(k) == 'string' then
			local num = mw.ustring.match(k, pattern)
			if num then
				nums[#nums + 1] = tonumber(num)
			end
		end
	end
	table.sort(nums)
	return nums
end

------------------------------------------------------------------------------------
-- numData
--
-- Dada uma tabela com chaves como {"foo1", "bar1", "foo2", "baz2"}, essa função 
-- retorna uma tabela de subtabelas no formato
-- {[1] = {foo = 'texto', bar = 'texto'}, [2] = {foo = 'texto', baz = 'texto'}}.
-- As chaves que não terminam com um número inteiro são armazenadas em uma 
-- subtabela chamada "other". A opção "compress" comprime a tabela para que ela 
-- possa ser iterada com "ipairs".
------------------------------------------------------------------------------------
function p.numData(t, compress)
	checkType('numData', 1, t, 'table')
	checkType('numData', 2, compress, 'boolean', true)
	local ret = {}
	for k, v in pairs(t) do
		local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
		if num then
			num = tonumber(num)
			local subtable = ret[num] or {}
			if prefix == '' then
				-- Os parâmetros posicionais correspondem à sequência ('string')
				-- em branco ('blank'); coloca-os no início da subtabela.
				prefix = 1
			end
			subtable[prefix] = v
			ret[num] = subtable
		else
			local subtable = ret.other or {}
			subtable[k] = v
			ret.other = subtable
		end
	end
	if compress then
		local other = ret.other
		ret = p.compressSparseArray(ret)
		ret.other = other
	end
	return ret
end

------------------------------------------------------------------------------------
-- compressSparseArray
--
-- Essa função pega um arranjo ('array') com um ou mais valores nulos ('nil') e  
-- remove os valores nulos ('nil') enquanto preserva a ordem, para que o arranjo
-- ('array')  possa ser percorrido com segurança com "ipairs".
------------------------------------------------------------------------------------
function p.compressSparseArray(t)
	checkType('compressSparseArray', 1, t, 'table')
	local ret = {}
	local nums = p.numKeys(t)
	for _, num in ipairs(nums) do
		ret[#ret + 1] = t[num]
	end
	return ret
end

------------------------------------------------------------------------------------
-- sparseIpairs
--
-- Esta função é uma iteradora para arranjos ('arrays') esparsos. Ela pode ser 
-- usada como "ipairs", mas pode manipular valores nulos ('nil').
------------------------------------------------------------------------------------
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

------------------------------------------------------------------------------------
-- size
--
-- Essa função retorna o tamanho de uma tabela de pares chave/valor. Ela também  
-- funcionará em arranjos ('arrays'), mas para arranjos ('arrays') é mais eficiente 
-- usar o operador #.
------------------------------------------------------------------------------------
function p.size(t)
	checkType('size', 1, t, 'table')
	local i = 0
	for _ in pairs(t) do
		i = i + 1
	end
	return i
end

local function defaultKeySort(item1, item2)
	-- número ("number") < sequência ("string"), então os números serão ordenados  
	-- antes das sequências ('strings').
	local type1, type2 = type(item1), type(item2)
	if type1 ~= type2 then
		return type1 < type2
	elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then
		return tostring(item1) < tostring(item2)
	else
		return item1 < item2
	end
end
------------------------------------------------------------------------------------
-- keysToList
--
-- Essa função retorna um arranjo ('array') das chaves em uma tabela, ordenada 
-- usando uma função de comparação padrão ou uma função "keySort" personalizada.
------------------------------------------------------------------------------------
function p.keysToList(t, keySort, checked)
	if not checked then
		checkType('keysToList', 1, t, 'table')
		checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'})
	end

	local arr = {}
	local index = 1
	for k in pairs(t) do
		arr[index] = k
		index = index + 1
	end

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

	return arr
end

------------------------------------------------------------------------------------
-- sortedPairs
--
-- Essa função itera por meio de uma tabela, com as chaves ordenadas usando a 
-- função "keysToList". Se houver apenas chaves numéricas, "sparseIpairs" 
-- provavelmente será mais eficiente.
------------------------------------------------------------------------------------
function p.sortedPairs(t, keySort)
	checkType('sortedPairs', 1, t, 'table')
	checkType('sortedPairs', 2, keySort, 'function', true)

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

	local i = 0
	return function ()
		i = i + 1
		local key = arr[i]
		if key ~= nil then
			return key, t[key]
		else
			return nil, nil
		end
	end
end

------------------------------------------------------------------------------------
-- isArray
--
-- Essa função retorna "true" se o valor fornecido for uma tabela e todas as chaves  
-- forem números inteiros consecutivos começando em 1.
------------------------------------------------------------------------------------
function p.isArray(v)
	if type(v) ~= 'table' then
		return false
	end
	local i = 0
	for _ in pairs(v) do
		i = i + 1
		if v[i] == nil then
			return false
		end
	end
	return true
end

------------------------------------------------------------------------------------
-- isArrayLike
--
-- Essa função retorna "true" se o valor fornecido for iterável e todas as chaves 
-- forem números inteiros consecutivos começando em 1.
------------------------------------------------------------------------------------
function p.isArrayLike(v)
	if not pcall(pairs, v) then
		return false
	end
	local i = 0
	for _ in pairs(v) do
		i = i + 1
		if v[i] == nil then
			return false
		end
	end
	return true
end

------------------------------------------------------------------------------------
-- invert
--
-- Essa função transpõe as chaves e valores em um arranjo ('array'). Por exemplo, 
-- {"a", "b", "c"} -> {a = 1, b = 2, c = 3}. As duplicatas não são suportadas (os 
-- valores dos resultados referem-se ao índice da última duplicata) e os valores 
-- "NaN" são ignorados.
------------------------------------------------------------------------------------
function p.invert(arr)
	checkType("invert", 1, arr, "table")
	local isNan = p.isNan
	local map = {}
	for i, v in ipairs(arr) do
		if not isNan(v) then
			map[v] = i
		end
	end

	return map
end

------------------------------------------------------------------------------------
-- listToSet
--
-- Essa função cria um conjunto a partir da parte do arranjo ('array') da tabela. 
-- A indexação do conjunto por qualquer um dos valores do arranjo ('array') retorna
-- "true". Por exemplo, {"a", "b", "c"} -> {a = true, b = true, c = true}. Os
-- valores "NaN" são ignorados, pois Lua os considera como nunca sendo iguais a
-- qualquer valor (incluindo outros "NaN"s ou até eles mesmos).
------------------------------------------------------------------------------------
function p.listToSet(arr)
	checkType("listToSet", 1, arr, "table")
	local isNan = p.isNan
	local set = {}
	for _, v in ipairs(arr) do
		if not isNan(v) then
			set[v] = true
		end
	end

	return set
end

------------------------------------------------------------------------------------
-- deepCopy
--
-- Função de cópia profunda recursiva. Preserva as identidades das subtabelas.
------------------------------------------------------------------------------------
local function _deepCopy(orig, includeMetatable, already_seen)
	-- Armazena as cópias das tabelas indexadas pela tabela original.
	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
--
-- Essa função concatena todos os valores na tabela que são indexados por um número,
-- em ordem.
-- "sparseConcat{a, nil, c, d}"  =>  "acd"
-- "sparseConcat{nil, b, c, d}"  =>  "bcd"
------------------------------------------------------------------------------------
function p.sparseConcat(t, sep, i, j)
	local arr = {}

	local arr_i = 0
	for _, v in p.sparseIpairs(t) do
		arr_i = arr_i + 1
		arr[arr_i] = v
	end

	return table.concat(arr, sep, i, j)
end

------------------------------------------------------------------------------------
-- length
--
-- Essa função localiza o comprimento de um arranjo ('array') ou de um 
-- quase-arranjo ('array') com chaves como "data1", "data2" etc., usando um
-- algoritmo de pesquisa exponencial. Ela é semelhante ao operador #, mas pode 
-- retornar um valor diferente quando houver lacunas na parte do arranjo ('array') 
-- da tabela. Destina-se a ser usada em dados carregados com "mw.loadData". Para 
-- outras tabelas, use #.
-- Observação: "#frame.args" no objeto "frame" sempre será definido como 0, 
-- independentemente do número de parâmetros de predefinição sem nome, portanto,
-- use esta função para "frame.args".
------------------------------------------------------------------------------------
function p.length(t, prefix)
	-- Exige módulo embutido, de modo que 
	-- [[Módulo:Testes/Gkiyoshinishimoto/Exponential search]] que é necessário 
	-- somente para esta função, não obtenha milhões de transclusões.
	local expSearch = require("Módulo:Testes/Gkiyoshinishimoto/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

------------------------------------------------------------------------------------
-- inArray
--
-- Essa função Retorna "true" se "valueToFind" for um membro do arranjo ('array')
-- e "false" se não.
------------------------------------------------------------------------------------
function p.inArray(arr, valueToFind)
	checkType("inArray", 1, arr, "table")
	-- se "valueToFind" for nulo ('nil'), erro?

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

return p