如何检查两个表(对象)在Lua中是否具有相同的值

时间:2013-12-02 09:54:17

标签: lua lua-table

我想检查两个表是否在Lua中具有相同的值,但是没有找到方法。

我使用运算符==,它似乎只是检查相同的对象,而不是表中的元素。

如果我有两张桌子,

a={}
b={}

a==b的值为false

但是如果

a={}
b=a

a==b的值为true

我想知道是否有办法检查Lua中具有相同元素的两个表。是否有像table.equals()这样的内置函数来检查?

6 个答案:

答案 0 :(得分:8)

没有用于按内容比较表的内置函数。

你必须自己写。您需要决定是要逐个或深入地按内容比较表。 有关一些想法,请参阅https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3

答案 1 :(得分:7)

我为Rutrus解决方案提供了一些改进。

function equals(o1, o2, ignore_mt)
    if o1 == o2 then return true end
    local o1Type = type(o1)
    local o2Type = type(o2)
    if o1Type ~= o2Type then return false end
    if o1Type ~= 'table' then return false end

    if not ignore_mt then
        local mt1 = getmetatable(o1)
        if mt1 and mt1.__eq then
            --compare using built in method
            return o1 == o2
        end
    end

    local keySet = {}

    for key1, value1 in pairs(o1) do
        local value2 = o2[key1]
        if value2 == nil or equals(value1, value2, ignore_mt) == false then
            return false
        end
        keySet[key1] = true
    end

    for key2, _ in pairs(o2) do
        if not keySet[key2] then return false end
    end
    return true
end

请注意,此解决方案并未考虑自引用。你可以使用pequals(下面)。当你的代码中有一些技巧时,这很有用。 但是不要使用这种方法进行定期检查!它变慢了。此外,如果您的对象具有自引用,则应重新分析您的结构。自我引用可能是糟糕架构的标志。

local function internalProtectedEquals(o1, o2, ignore_mt, callList)
    if o1 == o2 then return true end
    local o1Type = type(o1)
    local o2Type = type(o2)
    if o1Type ~= o2Type then return false end
    if o1Type ~= 'table' then return false end

    -- add only when objects are tables, cache results
    local oComparisons = callList[o1]
    if not oComparisons then
        oComparisons = {}
        callList[o1] = oComparisons
    end
    -- false means that comparison is in progress
    oComparisons[o2] = false

    if not ignore_mt then
        local mt1 = getmetatable(o1)
        if mt1 and mt1.__eq then
            --compare using built in method
            return o1 == o2
        end
    end

    local keySet = {}
    for key1, value1 in pairs(o1) do
        local value2 = o2[key1]
        if value2 == nil then return false end

        local vComparisons = callList[value1]
        if not vComparisons or vComparisons[value2] == nil then
            if not internalProtectedEquals(value1, value2, ignore_mt, callList) then
                return false
            end
        end

        keySet[key1] = true
    end

    for key2, _ in pairs(o2) do
        if not keySet[key2] then
            return false
        end
    end

    -- comparison finished - objects are equal do not compare again
    oComparisons[o2] = true
    return true
end

function pequals(o1, o2, ignore_mt)
    return internalProtectedEquals(o1, o2, ignore_mt, {})
end

您也可以分析CompareTables on lua wiki

答案 2 :(得分:2)

如果您实际上想测试简单的表,请尝试...

function do_tables_match( a, b )
    return table.concat(a) == table.concat(b)
end

在单独的注释上,与您的特定示例进行比较的内容如下...

function is_table_empty( table_to_test )
    -- Doesn't work
    return table_to_test == {}
    -- Works only if the table is numeric keyed with no gaps
    return #table_to_test = 0 
    -- Works!
    return next( table_to_test ) ~= nil 
end

答案 3 :(得分:1)

顺便说一下,我检查了@lhf链接并且坏了,我找到了这个有用的例子:

function is_table_equal(t1,t2,ignore_mt)
   local ty1 = type(t1)
   local ty2 = type(t2)
   if ty1 ~= ty2 then return false end
   -- non-table types can be directly compared
   if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
   -- as well as tables which have the metamethod __eq
   local mt = getmetatable(t1)
   if not ignore_mt and mt and mt.__eq then return t1 == t2 end
   for k1,v1 in pairs(t1) do
      local v2 = t2[k1]
      if v2 == nil or not is_table_equal(v1,v2) then return false end
   end
   for k2,v2 in pairs(t2) do
      local v1 = t1[k2]
      if v1 == nil or not is_table_equal(v1,v2) then return false end
   end
   return true
end

答案 4 :(得分:0)

我目前正在使用

local tableCompare
do
    local compare
    compare = function(src, tmp, _reverse)
        if (type(src) ~= "table" or type(tmp) ~= "table") then
            return src == tmp
        end

        for k, v in next, src do
            if type(v) == "table" then
                if type(tmp[k]) ~= "table" or not compare(v, tmp[k]) then
                    return false
                end
            else
                if tmp[k] ~= v then
                    return false
                end
            end
        end
        return _reverse and true or compare(tmp, src, true)
    end
    tableCompare = function(src, tmp, checkMeta)
        return compare(src, tmp) and (not checkMeta or compare(getmetatable(src), getmetatable(tmp)))
    end
end

print(tableCompare({ 1 , b = 30 }, { b = 30, 1 }, false))

答案 5 :(得分:0)

如果您只想比较两个小表,则可以(ab)使用inspect

local ins = require 'inspect'
local assert_equal = require 'luassert' .equal

assert_equal(ins({ 1 , b = 30 }), ins({ b = 30, 1 }))

此方法利用了inspect在序列化对象时对表元素进行排序的优势。而cjson则没有,这使得它在这种情况下不可用。