Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

lua

lua-table

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 not the elements in the table.

If I have two tables,

a={}
b={}

the value of a==b is false.

but if

a={}
b=a

the value of a==b is true.

I wonder know if there a way to check two tables having the same elements in Lua. Is there a built-in function like table.equals() to check?

like image 941
LetsOMG Avatar asked Dec 02 '13 09:12

LetsOMG


People also ask

Are Lua tables Hashmaps?

A Lua table is a collection of key-value pairs where the keys are unique and neither the key nor the value is nil . As such, a Lua table can resemble a dictionary, hashmap or associative array from other languages.

What is a table value Lua?

A table is a Lua data type that can store multiple values including numbers, booleans, strings, functions, and more. Tables are constructed with curly braces ( {} ) as shown here: Code Sample Expected Output Expand.


2 Answers

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.

like image 180
lhf Avatar answered Sep 23 '22 01:09

lhf


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.

like image 43
igv Avatar answered Sep 25 '22 01:09

igv