Module:TableTools: Difference between revisions
From Tardis Wiki, the free Doctor Who reference
add isNan function, shallowClone function and removeDuplicates function, fix up valueIntersection function to work properly for NaNs
(clone tn rather than returning an altered tn) |
(add isNan function, shallowClone function and removeDuplicates function, fix up valueIntersection function to work properly for NaNs) |
||
Line 25: | Line 25: | ||
-- isPositiveInteger | -- isPositiveInteger | ||
-- | -- | ||
-- This function returns true if the given | -- 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 | ||
Line 31: | Line 31: | ||
------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ||
--]] | --]] | ||
function p.isPositiveInteger( | function p.isPositiveInteger(v) | ||
if type( | if type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity then | ||
return true | return true | ||
else | else | ||
return false | return false | ||
end | end | ||
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) | |||
if type(v) == 'number' and tostring(v) == '-nan' then | |||
return true | |||
else | |||
return false | |||
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) | |||
local ret = {} | |||
for k, v in pairs(t) do | |||
ret[k] = v | |||
end | |||
return ret | |||
end | |||
--[[ | |||
------------------------------------------------------------------------------------ | |||
-- removeDuplicates | |||
-- | |||
-- This removes duplicate values from an array. Non-positive-integer keys are | |||
-- ignored. The earliest value is kept, and all subsequent duplicate values are | |||
-- removed, but otherwise the array order is unchanged. | |||
------------------------------------------------------------------------------------ | |||
--]] | |||
function p.removeDuplicates(t) | |||
local isNan = p.isNan | |||
local ret, exists = {}, {} | |||
for i, v in ipairs(t) do | |||
if isNan(v) then | |||
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence. | |||
ret[#ret + 1] = v | |||
else | |||
if not exists[v] then | |||
ret[#ret + 1] = v | |||
exists[v] = true | |||
end | |||
end | |||
end | |||
return ret | |||
end | end | ||
Line 167: | Line 228: | ||
function p.valueIntersection(...) | function p.valueIntersection(...) | ||
local lim = select('#', ...) | local lim = select('#', ...) | ||
if lim | if lim < 2 then | ||
error(" | error(lim .. ' argument' .. (lim == 1 and '' or 's') .. " passed to 'intersection' (minimum is 2)", 2) | ||
end | end | ||
local isNan = p.isNan | |||
local vals, ret = {}, {} | local vals, ret = {}, {} | ||
local isSameTable = true -- Tracks table equality. | |||
local tableTemp -- Used to store the table from the previous loop so that we can check table equality. | |||
for i = 1, lim do | for i = 1, lim do | ||
local t = select(i, ...) | local t = select(i, ...) | ||
checkType('valueIntersection', i, t, 'table') | checkType('valueIntersection', i, t, 'table') | ||
if tableTemp and t ~= tableTemp then | |||
isSameTable = false | |||
end | |||
tableTemp = t | |||
for k, v in pairs(t) do | for k, v in pairs(t) do | ||
if | -- NaNs are never equal to any other value, so they can't be in the intersection. | ||
v = | -- Which is lucky, as they also can't be table keys. | ||
if not isNan(v) then | |||
local valCount = vals[v] or 0 | |||
vals[v] = valCount + 1 | |||
end | end | ||
end | end | ||
end | |||
if isSameTable then | |||
-- If all the tables are equal, then the intersection is that table (including NaNs). | |||
-- All we need to do is convert it to an array and remove duplicate values. | |||
for k, v in pairs(tableTemp) do | |||
ret[#ret + 1] = v | |||
end | |||
return p.removeDuplicates(ret) | |||
end | end | ||
for val, count in pairs(vals) do | for val, count in pairs(vals) do | ||
if count == lim then | if count == lim then | ||
ret[#ret + 1] = val | ret[#ret + 1] = val | ||
end | end |