Cuando comparamos objetos en JavaScript estamos comparando que la referencia sea la misma (que ambos objetos apunten a la misma dirección de memoria), no los valores.
const mauricio = {
name: "Mauricio"
};
const anotherPerson = {
name: "Mauricio"
};
console.log( mauricio == anotherPerson ); //false
console.log( mauricio === anotherPerson ); //false
//aunque los valores son los mismos, la referencia no lo es
const anotherPerson3 = {
...mauricio
};
console.log( mauricio == anotherPerson3 ); //false
//la refencia no es la misma
const anotherPerson2 = mauricio;
console.log( mauricio == anotherPerson2 ); //true
//la referencia de memoria es la misma
El lenguaje no tiene de forma nativa una función o método para determinar que dos objetos tengan el mismo valor. Hay distintas soluciones que podemos implementar, aquí las veremos yendo desde las más básicas hasta las más complejas y robustas.
comparación de strings JSON
const person1 = {
name: "David",
age: "Ortega"
};
const person2 = {
name: "David",
age: "Ortega"
};
console.log( JSON.stringify( person1 ) === JSON.stringify( person2 ) ); //true
Lo que hacemos aquí es transformar los objetos en strings (los cuales sí son comparables por su valor). Es una solución valida, pero en el caso de que los atributos estén en otro orden dará false
porque el string sería distinto.
const person1 = {
age: "Ortega",
name: "David"
};
const person2 = {
name: "David",
age: "Ortega"
};
console.log( JSON.stringify( person1 ) === JSON.stringify( person2 ) ); //false
esto último hace que esta no sea una implementación muy robusta.
Comparación semi-automática usando arrays
Lo que haremos será colocar los objetos que queramos comparar en un array. Luego con el método filter
retornaremos todos los objetos que cumplan con las condiciones establecidas, si retorna dos objetos entonces sabemos que tienen el mismo valor.
const person1 = {
age: "Ortega",
name: "David"
};
const person2 = {
name: "David",
age: "Ortefga"
};
let objs = [ person1, person2 ];
let objComparasion = objs.filter( (obj) => {
return (
obj.name === person1.name &&
obj.age === person1.age
);
});
if ( objComparasion.length === 2 ) console.log( true );
else console.log( false );
Con esta implementación solucionamos el problema de la anterior; ya que no importa en qué orden están los atributos, pero esta implementación tiene sus propios problemas: no es agnóstico a la estructura del objeto porque tenemos que escribir manualmente cada atributo.
Solución definitiva
Construiremos una función para comparar nuestros objetos.
subirá el nivel de complejidad.
- Lo primero que hacemos es obtener los
keys
de cada objeto. - luego verificamos que ambos objetos tengan la misma cantidad de
keys
(atributos), lógicamente, si no tienen la misma cantidad no son iguales entonces retornamosfalse
. - A continuación verificamos que el valor que corresponde a cada
key
sea el mismo en cada objeto, sino es así retornarafalse
.
const person1 = {
name: "David",
age: "Ortega"
};
const person2 = {
name: "David",
age: "Ortega"
};
function definitiveComparasion( obj1, obj2 )
{
keys1 = Object.keys( obj1 );
keys2 = Object.keys( obj2 );
if ( keys1.length !== keys2.length ) return false;
for ( key of keys1 ) {
let val1 = obj1[key];
let val2 = obj2[key];
if ( val1 !== val2 ) return false;
}
return true;
}
console.log( definitiveComparasion( person1, person2 ) ); //true
Esta es una gran solución a nuestro problema: conseguimos que fuera agnóstica al objeto; pero… en JS es bastante normal tener objetos dentro de objetos y esta función no nos resuelve ese problema.
<h1>Solución DEFINITIVA 2.0</h1>
Usaremos como base la función anterior y le agregaremos un par de líneas más. Primero debemos identificar cuando un valor es un objeto para lo que crearemos una función y además tenemos que ver verificar que el objeto que hay dentro del objeto sea igual al objeto dentro del otro objeto ¿Te suena a algo? Si, recursividad, usaremos recursividad en esta función.
- En cuanto a la función
isObject
no hay mucho que explicar, es algo que se sale del tema del tutorial entonces solo créeme… funciona. - La primera diferencia es que declaramos una variable que contendrá un booleano:
true
si el valor para lakey
actual en ambos objetos es un objeto yfalse
si no es así. - La condición que usamos en este caso es un poco compleja. Verificamos si ambos valores son objetos y (aquí viene la recursividad) que esos valores no sea iguales, en tal caso sabemos que los objetos padres no son iguales. también tomamos en cuenta el caso de que ambos valores no sean objetos y que tampoco sean iguales. Si se cumple cualquiera de las dos condiciones anteriores sabremos que los objetos padre no son iguales.
function isObject( obj )
{
return Object.prototype.toString.call(obj) === '[object Object]';
}
function godsComparasion( obj1, obj2 )
{
keys1 = Object.keys( obj1 );
keys2 = Object.keys( obj2 );
if ( keys1.length !== keys2.length ) return false;
for ( key of keys1 ) {
let val1 = obj1[key];
let val2 = obj2[key];
let areObjects = isObject( val1 ) && isObject( val2 );
if (
( areObjects && !godsComparasion( val1, val2 ) ) ||
( !areObjects && val1 !== val2 )
) return false;
}
return true;
}
const person3 = {
name: "David",
age: "Ortega",
family: {
dad: "Rodrigo",
mom: "Estela"
}
};
const person4 = {
name: "David",
age: "Ortega",
family: {
dad: "Rodrigo",
mom: "Estela"
}
};
console.log( godsComparasion( person4, person3 ) ); //true
Esta solución es realmente robusta. Incluso podemos comparar objetos con objetos dentro.
Te invito a que intentes modificar esta función para poder comparar arrays dentro de un objeto y compartir tu solucion en los comentarios.
Fundamentos de JavaScript 2018