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.
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" } }
andvar pessoa2 = { "nome" : "Maria", "curso" : { "nome" : "Dreamweaver", "ano" : "2008" } }
. The methodisEquivalent
returnsfalse
even though we are equal.– bio