Module:TableTools: Difference between revisions

add isNan function, shallowClone function and removeDuplicates function, fix up valueIntersection function to work properly for NaNs
m>Mr. Stradivarius
(clone tn rather than returning an altered tn)
m>Mr. Stradivarius
(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 number 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
Line 31: Line 31:
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]
--]]
function p.isPositiveInteger(num)
function p.isPositiveInteger(v)
if type(num) == 'number' and num >= 1 and floor(num) == num and num < infinity then
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 == 0 then
if lim < 2 then
error("no arguments passed to 'valueIntersection'", 2)
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 type(v) == 'number' and tostring(v) == '-nan' then
-- NaNs are never equal to any other value, so they can't be in the intersection.
v = nan -- NaN cannot be a table key, so use a proxy variable.
-- 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
local valCount = vals[v] or 0
vals[v] = valCount + 1
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
if val == nan then
-- This ensures that we output a NaN when we had one as input, although
-- they may have been generated in a completely different way.
val = 0/0
end
ret[#ret + 1] = val
ret[#ret + 1] = val
end
end
Anonymous user