Module:TableTools: Difference between revisions

    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