Как проверить, имеют ли две таблицы (объекты) одинаковое значение в Lua
Я хочу проверить, имеют ли две таблицы одно значение в Lua, но не нашли способ.
Я использую оператор ==
, кажется, просто проверить те же объекты, но не элементы в таблице.
Если у меня две таблицы,
a={}
b={}
значение a==b
равно false
.
но если
a={}
b=a
значение a==b
равно true
.
Интересно узнать, есть ли способ проверить две таблицы с одинаковыми элементами в Lua. Есть ли встроенная функция, например table.equals()
для проверки?
Ответы
Ответ 1
Нет встроенной функции для сравнения таблиц по содержанию.
Вам придется писать свои собственные. Вам нужно будет решить, хотите ли вы сравнивать таблицы по содержанию неглубоко или глубоко.
См. https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3 для некоторых идей.
Ответ 2
Я предлагаю некоторые улучшения для решения 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 на wua wiki.
Ответ 3
Если вы действительно хотите проверить простые таблицы, попробуйте это...
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
Ответ 4
Кстати, я проверил ссылку @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
Ответ 5
В настоящее время я пользуюсь этим
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))
Ответ 6
если вы просто хотите сравнить две маленькие таблицы, вы можете (ab) использовать inspect.
local ins = require 'inspect'
local assert_equal = require 'luassert' .equal
assert_equal(ins({ 1 , b = 30 }), ins({ b = 30, 1 }))
Этот подход использует преимущества того, что inspect
сортирует элементы таблицы при сериализации объекта. в то время как cjson нет, что делает его непригодным для использования в этом случае.