How to compare if two javascript objects are equal?

Asked

Viewed 12,404 times

10

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 equal?

4 answers

11


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

But here’s a basic approach to how to check the equivalence of objects:

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: http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html

  • 1

    Remember that this function would not work with an object inside another object. Something like the comparison of var pessoa1 = { "nome" : "Maria", "curso" : { "nome" : "Dreamweaver", "ano" : "2008" } } and var pessoa2 = { "nome" : "Maria", "curso" : { "nome" : "Dreamweaver", "ano" : "2008" } }. The method isEquivalent returns false even though we are equal.

11

A great question, Rodrigo! I will answer your question by improving two of the codes already made by colleagues, also giving you some other information about Javascript. In the end, I’ll show you a solution of my own, uniting everything I’ve been through.

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

=== and !==

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

== and !=

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

In doubt, always use the first pair.

In both of your comparisons performed 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: therefore, the objects have different references and are different objects. If they were literal values, direct comparison would be possible.

Now, for the real answer of 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 to compare ourselves.

However, what problems with the codes presented above?

Code 1

This code has the unfortunate question that it does not verify whether this object has, internally, other objects. So, if he ends up finding an object within what is being checked, he will check only the references (as I reported earlier), and the result will be false, even if both are really equal. I made some comments in the code to show the problems and 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, that code is just very confusing. It does not make an obvious check of the properties of objects (as the previous one did), and it uses many if, making it difficult to read and understand the code. Moreover, it uses, inside, equality operators that compare only the value (which can lead to inconsistencies, depending on the value compared! ). However, I did not understand very well how it works. As 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, at last, the code I believe to be the best for full comparison of complex objects. I did this using some techniques I learned in the book The Good Parts, by Douglas Crockford. I highly recommend reading it! It will be a code very much like the 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: recursiveness! 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’s it for today! I hope I’ve helped.

  • Very cool your response, grateful for the collaboration.

  • I liked how you handled the problem, but your role would fail in this case: console.log( equals({ y: [] }, { y: [] }) ); or console.log( equals({ y: {} }, { y: {} ) );, the problem happens because when checking if(object1 === object2) when the prop1.length is equal to 0, in this case of empty objects, it will check their reference, and if they are not in the same place in memory, they will be different. The way to solve this case would be to change if(object1 === object2) for if(object1 === object2 || (typeof prop1 === "object" || typeof prop2 === "object")).

  • I would use such a function to check when they are empty objects([] and {}), remembering that in javascript an Array is an object: const isObjectOrArray = (obj) => {&#xA; return (typeof obj === "object" && obj !== null);&#xA;}; the verification obj !== null is necessary because null is returned as "Object".

  • I found another flaw in objects with different keys, ex: const obj1 = { Comp1: { x: 0, y: 0 } };&#xA;const obj2 = { Comp2: { width: 0, height: 0 } };, will receive an error when assigning var prop2 = ... but this can be avoided by comparing their typeof upon entering the function if(typeof object1 !== typeof object2){return false;}

6

Same logic as the other answer using es6 (more modern 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 of Array..

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

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

  • Hello Julio! Your answer is in English and the code is not commented on. It will hardly be useful to someone who is learning. You can upgrade or erase?

  • 1

    Hello Sergio. Changed.

  • return objetoA[value] !== objetoB[value]; Would not be chave instead of value?

  • well noticed, I made some changes to make the code more readable and I forgot about that detail. Thanks for letting me know :)

1

Convert both to a JSON string and compare strings:

function compareObjects(obj1, obj2) {
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}

But if objects are with attributes in different orders this will fail, I recommend using the lodash isEqual (https://lodash.com/docs/#isEqual).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.