web-dev-qa-db-ja.com

Luaで2つのテーブル(オブジェクト)が同じ値を持っているかどうかを確認する方法

Luaで2つのテーブルの値が同じかどうかを確認したいのですが、方法が見つかりませんでした。

演算子_==_を使用していますが、同じオブジェクトをチェックしているように見えますが、テーブル内の要素はチェックしていません。

テーブルが2つある場合、

_a={}
b={}
_

_a==b_の値はfalseです。

しかし、

_a={}
b=a
_

_a==b_の値はtrueです。

Luaで同じ要素を持つ2つのテーブルをチェックする方法があるかどうか疑問に思います。チェックするtable.equals()のような組み込み関数はありますか?

14
LetsOMG

テーブルを内容ごとに比較するための組み込み関数はありません。

あなたはあなた自身を書かなければならないでしょう。テーブルを内容ごとに浅く比較するか、深く比較するかを決定する必要があります。いくつかのアイデアについては、 https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_ を参照してください。

8
lhf

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

また、分析することもできます lua wikiのCompareTables

7
igv

実際に単純なテーブルをテストしたい場合は、これを試してください...

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
David Sorkovsky

ちなみに、@ 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
1
Rutrus

私は現在これを使用しています

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
Lautenschlager

2つの小さなテーブルを比較したいだけの場合は、(ab)use inspect を使用できます。

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

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

このアプローチは、オブジェクトをシリアル化するときにinspectがテーブル要素をソートすることを利用しています。一方、 cjson はそうではないため、この場合は使用できません。

0
Nick X