How to compare if two javascript objects are the same?

3

We know that if I have two objects created in different variables, but with the same content, when comparing, javascript will return false.

Example

var objA = {a:'teste'}
var objB = {a:'teste'}

if (objA === objB) {...} // return false
if (objA == objB) {...} // return false 

So how do I know if the two objects are the same?

    
asked by anonymous 13.04.2018 / 20:46

4 answers

3

There is no simple way to do this in JavaScript, because internally the language has two types of approach to testing equalities. Primitive data such as strings and numerals are compared by their value, whereas objects, arrays, and dates are compared by their reference. This reference comparison basically checks whether the given object is referenced by the same memory space.

But here's a basic approach to how to check for object equivalence:

function isEquivalent(a, b) {
    // Create arrays of property names
    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // If number of properties is different,
    // objects are not equivalent
    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // If values of same property are not equal,
        // objects are not equivalent
        if (a[propName] !== b[propName]) {
            return false;
        }
    }

    // If we made it this far, objects
    // are considered equivalent
    return true;
}

Source: link

    
13.04.2018 / 20:56
3

A great question, Rodrigo! I will answer your question by improving two of the codes already made by colleagues, also passing you some other information about JavaScript. In the end, I'll show you my own solution, uniting everything I've had.

Unfortunately, unlike some other languages, JavaScript does not have the "equals" method (like C # or Java). In addition, this language has two distinct pairs of equality operators.

=== e! ==

They function as you would normally expect; if both sides of the equality are of the same type and have the same value, then the result is true. Otherwise, it returns false.

== e! =

JavaScript will coerce value between the two sides of equality without checking the type. If they have the same value, it returns true. Otherwise, it returns false. However, one should be very careful with these, as they make a very superficial check and often you can end up with an undesirable result.

When in doubt, always use the first pair .

In both of your comparisons, the returned value was false, because what you are trying to compare is a complex object, and it has only the reference to the object stored in the variable: so the objects have different references and are < in> different objects . If they were literal values, direct comparison would be possible.

Now, for the real answer to your question: as JavaScript does not have a specific method for comparing objects, and even the best equality operators ( === and! == ) do not work with complex objects, it is necessary to make a method for comparison ourselves.

However, what are the problems with the codes above?

Code 1

This code has the unfortunate question that it does not check whether this object has, internally, other objects. So if it ends up finding an object inside the one being checked, it will check only the references (as I mentioned earlier), and the result will be false even if both are really the same. I made some comments in the code to show the problems and the qualities.

function isEquivalent(a, b) {
    // Acredito que essa seja uma das qualidades do código.
    // É realizada a verificação do que o objeto possui internamente, e isso
    // é passado diretamente para a variável.
    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // Então, é feita a verificação se os dois objetos possuem o mesmo
    // número de propriedades. Muito bom! E tira muito trabalho caso não
    // tenham.
    if (aProps.length != bProps.length) {
        return false;
    }

    // Caso tenham o mesmo número, é realizada uma iteração por todas as
    // propriedades do objeto. Porém, como ressaltei antes, não busca por
    // objetos dentro de objetos, podendo retornar uma inconsistência.
    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        if (a[propName] !== b[propName]) {
            return false;
        }
    }

    return true;
}

Code 2

Well, this code is just plain confusing. It does not make an obvious check of object properties (like the previous one did), and it uses many if, making it difficult to read and understand the code. Moreover, it uses within it equality operators that compare only the value (which can lead to inconsistencies, depending on the value compared!). However, I do not quite understand how it works. Just like in the previous one, I made some comments about the code (with what I could understand):

function deepEqual(obj1 , obj2 , profund){
    // Nesse primeiro if, ele está verificando se os dois objetos são, realmente,
    // objects, além de fazer a comparação obj1 != obj2, que sempre retornará false,
    // a não ser que sejam valores literais e sejam iguais.
    // Desnecessário.
    if(typeof(obj1) == "object" && typeof(obj2)=='object' && obj1 != obj2){
    // Aqui, ele pega as propriedades dos dois objetos e verifica se possuem
    // a mesma quantidade, porém não as armazena em local algum.
       if(Object.keys(obj1).length == Object.keys(obj2).length){
    // Aqui, está realizando um for in, que passa por todas as propriedades
    // do objeto, inclusive os prototype. Pode dar algum resultado errado!
            for (var prop in obj1) {                                     

                if ((prop in obj2)){
    // Aqui, ele realiza a comparação dos dois objetos.
                    if(profund == true && obj1[prop] != obj2[prop]){
                        return false;
                        break
                    }else if(profund == false){
                        return true
                        break
                    }                   
                }             
            }
        return true;    
       }

    }else{
        return (obj1 === obj2);
    }
}

Now, finally , the code that I believe to be the best for full comparison of complex objects. I did this using some techniques I learned in Douglas Crockford's The Good Parts . Highly recommend the reading! It will be a code very much like Code 1 , but with the improvement that I believe was lacking in it. I use, for this, one of the best things created in the history of computing: recursion! Below is the code with comments.

var equals = function (object1, object2) {
    // Realiza a verificação das propriedades dos objetos.
    var prop1 = Object.getOwnPropertyNames(object1);
    var prop2 = Object.getOwnPropertyNames(object1);

    // Realiza a verificação se ambos objetos possuem o mesmo número de 
    // propriedades. Caso contrário, já retorna dizendo que são diferentes.
    if(prop1.length !== prop2.length)
        return false;

    // Aqui, está sendo verificado se o objeto possui alguma propriedade.
    // Será usado quando for chamada a função na sua forma recursiva,
    // para verificar valores literais.
    if(prop1.length === 0)
        if(object1 === object2)
            return true;
        else
            return false;

    // Se forem iguais, realiza uma iteração por todas as propriedades.
    for(var i = 0; i < prop1.length; i++) {
    // Guarda o valor da propriedade atual na variável "prop".
        var prop = prop1[i];

    // Aqui está o pulo do gato.
    // Verifica se o valor e o tipo das duas propriedades são iguais.
    // Se sim, somente pula para a próxima iteração. Caso contrário,
    // podem ser duas coisas: ou são realmente distintos, ou é um objeto,
    // que, ao comparar as referências, retorna sempre falso.
    // Para ter certeza da informação, é chamada a mesma função de forma
    // recursiva, mandando, por parâmetro, os dois objetos que ficou a dúvida.
    // Se forem iguais, ou se tiverem mais algum objeto internamente, 
    // a função continuará a ser chamada recursivamente, até chegar ao
    // ponto de ser um valor literal. Ou, então, retornará falso, pois não
    // são iguais.
    // Caso sejam iguais, a função só continuará para a próxima iteração.
    // Caso contrário, a função já informa que não são dois objetos iguais.
        if(object1[prop] !== object2[prop]){
            if(equals(object1[prop], object2[prop]))
                continue;
            else
                return false;
        }
    }
    // Se chegou até aqui e aguentou todas as verificações...
    // Os objetos são iguais!
    return true;
}

That was it for today! I hope I have helped.

    
16.04.2018 / 04:31
1

The same logic as the other answer using es6 (most recent version of javascript) :

function saoIguais(objetoA, objetoB) {

    //Busca as chaves do objetoA e objetoB
    //utilizando o "let" o escopo da variável é limitado para o bloco.
    //Object.keys retornará um array com todas as chaves do objeto.
    let aChaves = Object.keys(objetoA),
        bChaves = Object.keys(objetoB);

    //Compara os tamanhos, se forem diferentes retorna falso pois 
    //o numero de propriedades é diferente, logo os objetos são diferentes
    if (aChaves.length != bChaves.length) {
        return false;
    }

    //Verifico se existe algum elemento com valor diferente nos objetos.
    //o array.some executa uma função(passada por parâmetro) para cada valor
    //do array. Essa função deve executar um teste, se para algum dos valores
    //o teste é verdadeiro, a execução é interrompida e true é retornado.
    //Do contrário, se o teste nunca for verdadeiro ele retornará false 
    //após executar o teste para todos valores do array.
    //Estou basicamente verficando se existe diferença entre dois valores do objeto.

    let saoDiferentes = aChaves.some((chave) => {
        return objetoA[chave] !== objetoB[chave];
    });

    //como saoDiferentes contém true caso os objetos sejam diferentes eu 
    //simplesmente nego esse valor para retornar que os objetos são iguais (ou não).
    return !saoDiferentes;
}

Documentation for Array.some .

ES6 brings new features and improvements to javascript. You can check out more about it here . (There are even newer versions already, see here ).

Particularly recommend studying because there are some very interesting things. In the newer versions there are fixes of problems that existed in the language, improvement in legibility and performance issues.

    
14.04.2018 / 02:20
0

Objects will always be considered different when compared by '==' or '==='. I ask myself: Why is it not possible to make such comparisons directly?  We can think of an object as an octopus that has several tentacles that grab values. At any moment we can cut these tentacles with delete or change what they are suddenly grabbing. It would be a monstrous inconsistency to say that one object is equal to the other.

An object is by itself unique. So this direct comparison to always return false was very well implemented.

However, it is possible to make comparisons between attributes and values, respectively, within objects.   For this I did the deepEqual() function that can receive beyond any value for comparison the parameter: profund that when true performs a check on the values of the object properties and when false only verifies if the two objects have the same properties.

    var objeto1 = {id: 1 , name: 'atila'}
    var objeto2 = {id: 1 , name: 'atila'}
    var objeto3 = {id: 2 , name: 'marcos'}
    

    function deepEqual(obj1 , obj2 , profund){
        if(typeof(obj1) == "object" && typeof(obj2)=='object' && obj1 != obj2){
           if(Object.keys(obj1).length == Object.keys(obj2).length){

                for (var prop in obj1) {                                     
                    
                    if ((prop in obj2)){
                        if(profund == true && obj1[prop] != obj2[prop]){
                            return false;
                            break
                        }else if(profund == false){
                            return true
                            break
                        }                   
                    }             
                }
            return true;    
           }

        }else{
            return (obj1 === obj2);
        }
    }

    result1 = deepEqual( objeto1 , objeto2, true);
    result2 = deepEqual( objeto1 , objeto3 , true);
    result3 = deepEqual( objeto1 , objeto3 , true);
    result4 = deepEqual( objeto1 , objeto3 , false);
    console.log('deepEqual( objeto1 , objeto2) = ' + result1)
    console.log('deepEqual( objeto1 , objeto3)= ' + result2)
    console.log('deepEqual( objeto1 , objeto3 , profund = true)= ' + result3)
    console.log('deepEqual( objeto1 , objeto3 , profund = false) = ' + result4)

If you really want to learn how the process works eloquently-javascript (book) will help you a lot in the process. At the end of the chapter they have a proposed exercise to build a function that compares deeply using the concept of recursion.

See more: link

    
13.04.2018 / 20:56