How to check if two tables(objects) have the same value in Lua

后端 未结 6 1573
粉色の甜心
粉色の甜心 2021-01-04 02:00

I wanna check if two tables have the same value in Lua, but didn\'t find the way.

I use the operator ==, it seems just to check the same objects, but no

相关标签:
6条回答
  • 2021-01-04 02:40

    If you actually want to test simple tables try this...

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

    On a separate note, something that compares to your specific example as follows...

    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
    
    0 讨论(0)
  • 2021-01-04 02:41

    There is no built-in function for comparing tables by contents.

    You'll have to write your own. You'll need to decide whether you want to compare tables by contents shallowly or deeply. See https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3 for some ideas.

    0 讨论(0)
  • 2021-01-04 02:42

    By the way, I checked @lhf link and is broken, I found this useful example:

    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
    
    0 讨论(0)
  • 2021-01-04 02:50

    I currently use this

    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))
    
    0 讨论(0)
  • 2021-01-04 02:53

    if you just want to compare 2 small tables, you may (ab)use inspect.

    local ins = require 'inspect'
    local assert_equal = require 'luassert' .equal
    
    assert_equal(ins({ 1 , b = 30 }), ins({ b = 30, 1 }))
    

    This approach takes advantage of that inspect sorts table elements when serializing an object. while cjson doesn't, which makes it unusable in this case.

    0 讨论(0)
  • 2021-01-04 02:56

    I offer some improvements for Rutrus solution.

    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
    

    Be aware that this solution doesn't take into account self references. You may use pequals (below). It's usefull when you have some tricks in your code. But do not use this method for regular checks! It's slower. Also if your object has self reference, you should reanalyze your structure. Self references can be a sign of bad architecture.

    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
    

    Also you may analyze CompareTables on lua wiki.

    0 讨论(0)
提交回复
热议问题