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

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

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

如果我有两个表,

a={}
b={}

a==b 的值为 false

但如果

a={}
b=a

a==b 的值为 true

我想知道是否有一种方法可以检查 Lua 中具有相同元素的两个表。是否有内置函数类似于 table.equals() 来检查?

点赞
用户107090
用户107090

没有预设函数用于比较表格的内容。

你需要自己编写。你需要决定是浅层比较还是深层比较表格内容。 可以参考https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3获取一些想法。

2013-12-02 10:47:21
用户1821558
用户1821558

顺便说一下,我检查了 @lhf 的链接,发现它已经失效了,我找到了这个有用的例子:

function is_table_equal(t1, t2, ignore_mt)
   local ty1 = type(t1)
   local ty2 = type(t2)
   if ty1 ~= ty2 then return false end
   -- 非table类型可以直接比较
   if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
   -- 也可以比较有__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
2015-06-10 13:12:03
用户178911
用户178911

我对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
            --使用内置方法进行比较
            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

    --仅在对象为表时添加,缓存结果
    local oComparisons = callList[o1]
    if not oComparisons then
        oComparisons = {}
        callList[o1] = oComparisons
    end
    --false表示比较正在进行中
    oComparisons[o2] = false

    if not ignore_mt then
        local mt1 = getmetatable(o1)
        if mt1 and mt1.__eq then
            --使用内置方法进行比较
            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

    --比较完成-对象相等,不要再比较
    oComparisons[o2] = true
    return true
end

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

您也可以分析Lua Wiki上的CompareTables

2015-09-18 20:47:27
用户1142299
用户1142299

如果您真的想测试简单的表格,请尝试以下...

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

另外,有些与您的特定示例相比的内容如下...

function is_table_empty( table_to_test )
    -- 不起作用
    return table_to_test == {}
    -- 仅在表格具有数字键而没有空隙的情况下起作用
    return #table_to_test = 0
    -- 起作用!
    return next( table_to_test ) ~= nil
end
2019-01-11 03:53:37
用户10637463
用户10637463

我目前使用这个:

local tableCompare
do
    -- 定义局部函数 compare
    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
    tableCompare = function(src, tmp, checkMeta)
        -- 调用 compare 函数,检查元表
        return compare(src, tmp) and (not checkMeta or compare(getmetatable(src), getmetatable(tmp)))
    end
end

-- 输出 true
print(tableCompare({ 1 , b = 30 }, { b = 30, 1 }, false))
2019-08-24 22:38:24
用户590307
用户590307

如果你只是想比较两个小表格,你可以滥用 inspect

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

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

这种方法利用了 inspect 在序列化对象时对表格元素进行排序的优点,而 cjson则不会进行排序,这使其在这种情况下无法使用。

2019-09-19 17:04:43