metodos - ¿Cómo determinar la igualdad para dos objetos JavaScript?
object to array javascript (30)
¿Estás tratando de probar si dos objetos son iguales? Es decir: sus propiedades son iguales?
Si este es el caso, probablemente habrás notado esta situación:
var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"
Es posible que tengas que hacer algo como esto:
function objectEquals(obj1, obj2) {
for (var i in obj1) {
if (obj1.hasOwnProperty(i)) {
if (!obj2.hasOwnProperty(i)) return false;
if (obj1[i] != obj2[i]) return false;
}
}
for (var i in obj2) {
if (obj2.hasOwnProperty(i)) {
if (!obj1.hasOwnProperty(i)) return false;
if (obj1[i] != obj2[i]) return false;
}
}
return true;
}
Obviamente, esa función podría funcionar con un poco de optimización y la capacidad de realizar una comprobación profunda (para manejar objetos anidados: var a = { foo : { fu : "bar" } }
) pero entiendes la idea.
Como señaló FOR, es posible que tenga que adaptar esto para sus propios propósitos, por ejemplo: diferentes clases pueden tener diferentes definiciones de "igual". Si solo está trabajando con objetos simples, lo anterior puede ser suficiente, de lo contrario, una función personalizada MyClass.equals()
puede ser la MyClass.equals()
.
Un operador de igualdad estricta le dirá si dos tipos de objeto son iguales. Sin embargo, ¿hay una manera de saber si dos objetos son iguales, al igual que el valor del código hash en Java?
Pregunta de desbordamiento de pila ¿Existe algún tipo de función hashCode en JavaScript? Es similar a esta pregunta, pero requiere una respuesta más académica. El escenario anterior demuestra por qué sería necesario tener uno, y me pregunto si hay alguna solución equivalente .
¿Por qué reinventar la rueda? Dale a Lodash una oportunidad. Tiene una serie de funciones Lodash como Lodash .
_.isEqual(object, other);
Revisará la fuerza bruta de cada valor clave, al igual que los otros ejemplos en esta página, utilizando ECMAScript 5 y optimizaciones nativas si están disponibles en el navegador.
Nota: Anteriormente, esta respuesta recomendaba Underscore.js , pero lodash ha hecho un mejor trabajo para solucionar los errores y resolver los problemas con coherencia.
Aconsejaría contra el hash o la serialización (como sugiere la solución JSON). Si necesita probar si dos objetos son iguales, entonces debe definir qué es igual a la media. Podría ser que todos los miembros de datos en ambos objetos coincidan, o podría ser que las ubicaciones de la memoria coincidan (lo que significa que ambas variables hacen referencia al mismo objeto en la memoria), o puede ser que solo un miembro de datos en cada objeto debe coincidir.
Recientemente desarrollé un objeto cuyo constructor crea una nueva identificación (comenzando desde 1 e incrementando en 1) cada vez que se crea una instancia. Este objeto tiene una función isEqual que compara ese valor de id con el valor de id de otro objeto y devuelve true si coinciden.
En ese caso definí "igual" en el sentido de que los valores de id coinciden. Dado que cada instancia tiene una identificación única, esto podría usarse para imponer la idea de que los objetos coincidentes también ocupan la misma ubicación de memoria. Aunque eso no es necesario.
Al necesitar una función de comparación de objetos más genérica de la que se había publicado, cociné lo siguiente. Crítica apreciada ...
Object.prototype.equals = function(iObj) {
if (this.constructor !== iObj.constructor)
return false;
var aMemberCount = 0;
for (var a in this) {
if (!this.hasOwnProperty(a))
continue;
if (typeof this[a] === ''object'' && typeof iObj[a] === ''object'' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
return false;
++aMemberCount;
}
for (var a in iObj)
if (iObj.hasOwnProperty(a))
--aMemberCount;
return aMemberCount ? false : true;
}
El método Object.is () determina si dos valores son el mismo valor.
Sintaxis
Object.is(value1, value2);
Parámetros
value1: El primer valor para comparar.
valor2: el segundo valor para comparar.
Valor de retorno
Un valor booleano que indica si los dos argumentos tienen o no el mismo valor.
Ejemplos
Object.is(''foo'', ''foo''); // true
Object.is(window, window); // true
Object.is(''foo'', ''bar''); // false
Object.is([], []); // false
var test = { a: 1 };
Object.is(test, test); // true
Object.is(null, null); // true
// Special Cases
Object.is(0, -0); // false
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
Si quieres más información lee esto, link
El operador de igualdad predeterminado en JavaScript para Objetos es verdadero cuando se refieren a la misma ubicación en la memoria.
var x = {};
var y = {};
var z = x;
x === y; // => false
x === z; // => true
Si necesita un operador de igualdad diferente, deberá agregar un método equals(other)
, o algo parecido a sus clases y los detalles de su dominio de problema determinarán qué significa eso exactamente.
Aquí hay un ejemplo de naipes:
function Card(rank, suit) {
this.rank = rank;
this.suit = suit;
this.equals = function(other) {
return other.rank == this.rank && other.suit == this.suit;
};
}
var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");
queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true
En Node.js, puede usar su require("assert").deepEqual
nativo require("assert").deepEqual
. Más información: http://nodejs.org/api/assert.html
Por ejemplo:
var assert = require("assert");
assert.deepEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError
Otro ejemplo que devuelve true
/ false
lugar de devolver errores:
var assert = require("assert");
function deepEqual(a, b) {
try {
assert.deepEqual(a, b);
} catch (error) {
if (error.name === "AssertionError") {
return false;
}
throw error;
}
return true;
};
Esta es mi versión. Está utilizando la nueva función Object.keys que se introdujo en ES5 e ideas / pruebas de + , + y + :
function objectEquals(x, y) {
''use strict'';
if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
// after this just checking type of one would be enough
if (x.constructor !== y.constructor) { return false; }
// if they are functions, they should exactly refer to same one (because of closures)
if (x instanceof Function) { return x === y; }
// if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
if (x instanceof RegExp) { return x === y; }
if (x === y || x.valueOf() === y.valueOf()) { return true; }
if (Array.isArray(x) && x.length !== y.length) { return false; }
// if they are dates, they must had equal valueOf
if (x instanceof Date) { return false; }
// if they are strictly equal, they both need to be object at least
if (!(x instanceof Object)) { return false; }
if (!(y instanceof Object)) { return false; }
// recursive object equality check
var p = Object.keys(x);
return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
p.every(function (i) { return objectEquals(x[i], y[i]); });
}
///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
if (x) { document.write(''<div style="color: green;">Passed</div>''); }
else { document.write(''<div style="color: red;">Failed</div>''); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));
assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));
assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));
assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));
assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
assertTrue = assert.isTrue;
assertFalse({}.equals(null));
assertFalse({}.equals(undefined));
assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));
assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));
assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));
assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
var a = {a: ''text'', b:[0,1]};
var b = {a: ''text'', b:[0,1]};
var c = {a: ''text'', b: 0};
var d = {a: ''text'', b: false};
var e = {a: ''text'', b:[1,0]};
var i = {
a: ''text'',
c: {
b: [1, 0]
}
};
var j = {
a: ''text'',
c: {
b: [1, 0]
}
};
var k = {a: ''text'', b: null};
var l = {a: ''text'', b: undefined};
assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));
// from comments on post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));
// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won''t have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));
Heres es una solución en ES6 / ES2015 utilizando un enfoque de estilo funcional:
const typeOf = x =>
({}).toString
.call(x)
.match(//[object (/w+)/]/)[1]
function areSimilar(a, b) {
const everyKey = f => Object.keys(a).every(f)
switch(typeOf(a)) {
case ''Array'':
return a.length === b.length &&
everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
case ''Object'':
return Object.keys(a).length === Object.keys(b).length &&
everyKey(k => areSimilar(a[k], b[k]));
default:
return a === b;
}
}
Implementación deepEqual
funcional de deepEqual
:
function deepEqual(x, y) {
return (x && y && typeof x === ''object'' && typeof y === ''object'') ?
(Object.keys(x).length === Object.keys(y).length) &&
Object.keys(x).reduce(function(isEqual, key) {
return isEqual && deepEqual(x[key], y[key]);
}, true) : (x === y);
}
Edición : versión 2, utilizando la sugerencia de jib y las funciones de flecha de ES6:
function deepEqual(x, y) {
const ok = Object.keys, tx = typeof x, ty = typeof y;
return x && y && tx === ''object'' && tx === ty ? (
ok(x).length === ok(y).length &&
ok(x).every(key => deepEqual(x[key], y[key]))
) : (x === y);
}
Me enfrenté al mismo problema y decidí escribir mi propia solución. Pero como también quiero comparar Arrays con Objetos y viceversa, elaboré una solución genérica. Decidí agregar las funciones al prototipo, pero uno puede reescribirlas fácilmente a funciones independientes. Aquí está el código:
Array.prototype.equals = Object.prototype.equals = function(b) {
var ar = JSON.parse(JSON.stringify(b));
var err = false;
for(var key in this) {
if(this.hasOwnProperty(key)) {
var found = ar.find(this[key]);
if(found > -1) {
if(Object.prototype.toString.call(ar) === "[object Object]") {
delete ar[Object.keys(ar)[found]];
}
else {
ar.splice(found, 1);
}
}
else {
err = true;
break;
}
}
};
if(Object.keys(ar).length > 0 || err) {
return false;
}
return true;
}
Array.prototype.find = Object.prototype.find = function(v) {
var f = -1;
for(var i in this) {
if(this.hasOwnProperty(i)) {
if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
if(this[i].equals(v)) {
f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
}
}
else if(this[i] === v) {
f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
}
}
}
return f;
}
Este algoritmo se divide en dos partes; La función equals en sí misma y una función para encontrar el índice numérico de una propiedad en una matriz / objeto. La función de búsqueda solo es necesaria porque indexof solo encuentra números y cadenas y no objetos.
Uno puede llamarlo así:
({a: 1, b: "h"}).equals({a: 1, b: "h"});
La función devuelve verdadero o falso, en este caso verdadero. El algoritmo als permite la comparación entre objetos muy complejos:
({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})
El ejemplo superior devolverá verdadero, incluso aunque las propiedades tengan un orden diferente. Un pequeño detalle a tener en cuenta: este código también verifica el mismo tipo de dos variables, por lo que "3" no es lo mismo que 3.
No sé si alguien ha publicado algo similar a esto, pero aquí hay una función que hice para verificar la igualdad de los objetos.
function objectsAreEqual(a, b) {
for (var prop in a) {
if (a.hasOwnProperty(prop)) {
if (b.hasOwnProperty(prop)) {
if (typeof a[prop] === ''object'') {
if (!objectsAreEqual(a[prop], b[prop])) return false;
} else {
if (a[prop] !== b[prop]) return false;
}
} else {
return false;
}
}
}
return true;
}
Además, es recursivo, por lo que también puede verificar la igualdad profunda, si así lo llaman.
Si está comparando objetos JSON, puede usar https://github.com/mirek/node-rus-diff
npm install rus-diff
Uso:
a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}
var rusDiff = require(''rus-diff'').rusDiff
console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { ''$set'': { ''foo.bar'': 2 } }
Si dos objetos son diferentes, se devuelve un objeto similar {$rename:{...}, $unset:{...}, $set:{...}}
compatible con MongoDB.
Si está trabajando en AngularJS , la función angular.equals
determinará si dos objetos son iguales. En Ember.js usar isEqual
.
-
angular.equals
- Consulte la docs o la source para obtener más información sobre este método. Hace una comparación profunda en matrices también. - Ember.js
isEqual
: consulte la docs o la source para obtener más información sobre este método. No hace una comparación profunda en matrices.
var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];
if(angular.equals(purple, drank)) {
document.write(''got dat'');
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
Si está utilizando ES6+ través de Babel o de otra manera, también puede usar Object.is(x, y)
.
Referencia: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y
Si está utilizando una biblioteca JSON, puede codificar cada objeto como JSON, luego comparar las cadenas resultantes para la igualdad.
var obj1={test:"value"};
var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
NOTA: Si bien esta respuesta funcionará en muchos casos, como varias personas han señalado en los comentarios, es problemático por varias razones. En casi todos los casos querrá encontrar una solución más robusta.
Si tiene a mano una función de copia en profundidad, puede usar el siguiente truco para seguir usando JSON.stringify
al JSON.stringify
tiempo que coincide con el orden de las propiedades:
function equals(obj1, obj2) {
function _equals(obj1, obj2) {
return JSON.stringify(obj1)
=== JSON.stringify($.extend(true, {}, obj1, obj2));
}
return _equals(obj1, obj2) && _equals(obj2, obj1);
}
Demostración: http://jsfiddle.net/CU3vb/3/
Razón fundamental:
Dado que las propiedades de obj1
se copian al clon una por una, se conservará su orden en el clon. Y cuando las propiedades de obj2
se copian al clon, ya que las propiedades que ya existen en obj1
simplemente se sobrescribirán, sus órdenes en el clon se conservarán.
Solo quería contribuir con mi versión de comparación de objetos utilizando algunas características de es6. No tiene en cuenta un pedido. Después de convertir todos if / else a ternary he venido con lo siguiente:
function areEqual(obj1, obj2) {
return Object.keys(obj1).every(key => {
return obj2.hasOwnProperty(key) ?
typeof obj1[key] === ''object'' ?
areEqual(obj1[key], obj2[key]) :
obj1[key] === obj2[key] :
false;
}
)
}
Una solución simple para este problema que muchas personas no se dan cuenta es ordenar las cadenas JSON (por carácter). Esto también suele ser más rápido que las otras soluciones mencionadas aquí:
function areEqual(obj1, obj2) {
var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
if (!a) a = '''';
if (!b) b = '''';
return (a.split('''').sort().join('''') == b.split('''').sort().join(''''));
}
Otra cosa útil acerca de este método es que puede filtrar comparaciones pasando una función de "reemplazo" a las funciones de JSON.stringify ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter ). Lo siguiente solo comparará todas las claves de objetos que se denominan "derp":
function areEqual(obj1, obj2, filter) {
var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
if (!a) a = '''';
if (!b) b = '''';
return (a.split('''').sort().join('''') == b.split('''').sort().join(''''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
return (key === ''derp'') ? value : undefined;
});
Utilizo esta función comparable
para producir copias de mis objetos que son comparables JSON:
var comparable = o => (typeof o != ''object'' || !o)? o :
Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});
// Demo:
var a = { a: 1, c: 4, b: [2, 3], d: { e: ''5'', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: ''5'' }, a: 1 };
console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>
Resulta útil en las pruebas (la mayoría de los marcos de prueba tienen una función). P.ej
is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), ''x must match y'');
Si se detecta una diferencia, las cadenas se registran, haciendo que las diferencias sean detectables:
x must match y
got {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.
puede usar _.isEqual(obj1, obj2)
de la biblioteca underscore.js.
Aquí hay un ejemplo:
var stooge = {name: ''moe'', luckyNumbers: [13, 27, 34]};
var clone = {name: ''moe'', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true
Consulte la documentación oficial desde aquí: http://underscorejs.org/#isEqual
La respuesta corta
La respuesta simple es: No, no hay medios genéricos para determinar que un objeto es igual a otro en el sentido que usted quiere decir. La excepción es cuando estás pensando estrictamente que un objeto no tiene tipo.
La respuesta larga
El concepto es el de un método Igual que compara dos instancias diferentes de un objeto para indicar si son iguales en un nivel de valor. Sin embargo, depende del tipo específico definir cómo se debe implementar un método Equals
. Una comparación iterativa de atributos que tienen valores primitivos puede no ser suficiente, puede haber atributos que no deben considerarse parte del valor del objeto. Por ejemplo,
function MyClass(a, b)
{
var c;
this.getCLazy = function() {
if (c === undefined) c = a * b // imagine * is really expensive
return c;
}
}
En este caso anterior, c
no es realmente importante para determinar si dos instancias de MyClass son iguales, solo a
y b
son importantes. En algunos casos, c
puede variar entre los casos y, sin embargo, no ser significativo durante la comparación.
Tenga en cuenta que este problema se aplica cuando los miembros también pueden ser instancias de un tipo y cada uno de ellos debería tener un medio para determinar la igualdad.
Lo que complica aún más las cosas es que, en JavaScript, la distinción entre datos y método es borrosa.
Un objeto puede hacer referencia a un método que debe llamarse como un controlador de eventos, y esto probablemente no se considere parte de su "estado de valor". Mientras que a otro objeto se le puede asignar una función que realiza un cálculo importante y, por lo tanto, hace que esta instancia sea diferente de otras simplemente porque hace referencia a una función diferente.
¿Qué pasa con un objeto que tiene uno de sus métodos de prototipo existentes anulados por otra función? ¿Podría seguir siendo igual a otra instancia que de otra manera idéntica? Esa pregunta solo puede ser respondida en cada caso específico para cada tipo.
Como se dijo anteriormente, la excepción sería un objeto estrictamente sin tipo. En cuyo caso, la única opción sensata es una comparación iterativa y recursiva de cada miembro. Incluso entonces uno tiene que preguntar cuál es el "valor" de una función?
Las soluciones más simples y lógicas para comparar todo como Object, Array, String, Int ...
JSON.stringify({a: val1}) === JSON.stringify({a: val2})
Nota:
- necesitas reemplazar
val1
yval2
con tu objeto - para el objeto, tiene que ordenar (por clave) recursivamente para ambos objetos laterales
Aquí hay una versión del truco Stringify que es menos tipográfica y funciona en muchos casos para comparaciones de datos JSON triviales.
var obj1Fingerprint = JSON.stringify(obj1).replace(//{|/}/g,'''').split('','').sort().join('','');
var obj2Fingerprint = JSON.stringify(obj2).replace(//{|/}/g,'''').split('','').sort().join('','');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
Estoy haciendo las siguientes suposiciones con esta función:
- Usted controla los objetos que está comparando y solo tiene valores primitivos (es decir, no objetos anidados, funciones, etc.).
- Su navegador tiene soporte para Object.keys .
Esto debe ser tratado como una demostración de una estrategia simple.
/**
* Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
* @param {Object} object1
* @param {Object} object2
* @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
* @returns {Boolean}
*/
function isEqual( object1, object2, order_matters ) {
var keys1 = Object.keys(object1),
keys2 = Object.keys(object2),
i, key;
// Test 1: Same number of elements
if( keys1.length != keys2.length ) {
return false;
}
// If order doesn''t matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
// keys1 = Object.keys({a:2, b:1}) = ["a","b"];
// keys2 = Object.keys({b:1, a:2}) = ["b","a"];
// This is why we are sorting keys1 and keys2.
if( !order_matters ) {
keys1.sort();
keys2.sort();
}
// Test 2: Same keys
for( i = 0; i < keys1.length; i++ ) {
if( keys1[i] != keys2[i] ) {
return false;
}
}
// Test 3: Values
for( i = 0; i < keys1.length; i++ ) {
key = keys1[i];
if( object1[key] != object2[key] ) {
return false;
}
}
return true;
}
Para comparar claves para pares de claves / valores simples, instancias de objetos, uso:
function compareKeys(r1, r2) {
var nloops = 0, score = 0;
for(k1 in r1) {
for(k2 in r2) {
nloops++;
if(k1 == k2)
score++;
}
}
return nloops == (score * score);
};
Una vez que se comparan las claves, un simple for..in
bucle adicional es suficiente.
La complejidad es O (N * N) con N es el número de teclas.
Espero / supongo que los objetos que defino no tienen más de 1000 propiedades ...
Al salir de mi biblioteca personal, que uso repetidamente para mi trabajo. La siguiente función es una indulgencia profunda igual recursiva, que no verifica
- Igualdad de clases
- Valores heredados
- Valores de igualdad estricta
Principalmente utilizo esto para verificar si obtengo respuestas iguales contra varias implementaciones de API. Donde la diferencia de implementación (como cadena vs número) y valores nulos adicionales, puede ocurrir.
Su implementación es bastante sencilla y corta (si se eliminan todos los comentarios)
/** Recursively check if both objects are equal in value
***
*** This function is designed to use multiple methods from most probable
*** (and in most cases) valid, to the more regid and complex method.
***
*** One of the main principles behind the various check is that while
*** some of the simpler checks such as == or JSON may cause false negatives,
*** they do not cause false positives. As such they can be safely run first.
***
*** # !Important Note:
*** as this function is designed for simplified deep equal checks it is not designed
*** for the following
***
*** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
*** - Inherited values, this actually ignores them
*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
*** - Performance across all cases. This is designed for high performance on the
*** most probable cases of == / JSON equality. Consider bench testing, if you have
*** more ''complex'' requirments
***
*** @param objA : First object to compare
*** @param objB : 2nd object to compare
*** @param .... : Any other objects to compare
***
*** @returns true if all equals, or false if invalid
***
*** @license Copyright by [email protected], 2012.
*** Licensed under the MIT license: http://opensource.org/licenses/MIT
**/
function simpleRecusiveDeepEqual(objA, objB) {
// Multiple comparision check
//--------------------------------------------
var args = Array.prototype.slice.call(arguments);
if(args.length > 2) {
for(var a=1; a<args.length; ++a) {
if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {
return false;
}
}
return true;
} else if(args.length < 2) {
throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
}
// basic equality check,
//--------------------------------------------
// if this succed the 2 basic values is equal,
// such as numbers and string.
//
// or its actually the same object pointer. Bam
//
// Note that if string and number strictly equal is required
// change the equality from ==, to ===
//
if(objA == objB) {
return true;
}
// If a value is a bsic type, and failed above. This fails
var basicTypes = ["boolean", "number", "string"];
if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
return false;
}
// JSON equality check,
//--------------------------------------------
// this can fail, if the JSON stringify the objects in the wrong order
// for example the following may fail, due to different string order:
//
// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
//
if(JSON.stringify(objA) == JSON.stringify(objB)) {
return true;
}
// Array equality check
//--------------------------------------------
// This is performed prior to iteration check,
// Without this check the following would have been considered valid
//
// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
//
// Note that u may remove this segment if this is what is intended
//
if( Array.isArray(objA) ) {
//objA is array, objB is not an array
if( !Array.isArray(objB) ) {
return false;
}
} else if( Array.isArray(objB) ) {
//objA is not array, objB is an array
return false;
}
// Nested values iteration
//--------------------------------------------
// Scan and iterate all the nested values, and check for non equal values recusively
//
// Note that this does not check against null equality, remove the various "!= null"
// if this is required
var i; //reuse var to iterate
// Check objA values against objB
for (i in objA) {
//Protect against inherited properties
if(objA.hasOwnProperty(i)) {
if(objB.hasOwnProperty(i)) {
// Check if deep equal is valid
if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
return false;
}
} else if(objA[i] != null) {
//ignore null values in objA, that objB does not have
//else fails
return false;
}
}
}
// Check if objB has additional values, that objA do not, fail if so
for (i in objB) {
if(objB.hasOwnProperty(i)) {
if(objB[i] != null && !objA.hasOwnProperty(i)) {
//ignore null values in objB, that objA does not have
//else fails
return false;
}
}
}
// End of all checks
//--------------------------------------------
// By reaching here, all iteration scans have been done.
// and should have returned false if it failed
return true;
}
// Sanity checking of simpleRecusiveDeepEqual
(function() {
if(
// Basic checks
!simpleRecusiveDeepEqual({}, {}) ||
!simpleRecusiveDeepEqual([], []) ||
!simpleRecusiveDeepEqual([''a''], [''a'']) ||
// Not strict checks
!simpleRecusiveDeepEqual("1", 1) ||
// Multiple objects check
!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
// Ensure distinction between array and object (the following should fail)
simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
// Null strict checks
simpleRecusiveDeepEqual( 0, null ) ||
simpleRecusiveDeepEqual( "", null ) ||
// Last "false" exists to make the various check above easy to comment in/out
false
) {
alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
} else {
//added this last line, for SO snippet alert on success
alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
}
})();
Es útil considerar dos objetos iguales si tienen todos los mismos valores para todas las propiedades y recursivamente para todos los objetos y matrices anidados. También considero iguales los siguientes dos objetos:
var a = {p1: 1};
var b = {p1: 1, p2: undefined};
De manera similar, las matrices pueden tener elementos "faltantes" y elementos indefinidos. Yo trataría a los mismos también:
var c = [1, 2];
var d = [1, 2, undefined];
Una función que implementa esta definición de igualdad:
function isEqual(a, b) {
if (a === b) {
return true;
}
if (generalType(a) != generalType(b)) {
return false;
}
if (a == b) {
return true;
}
if (typeof a != ''object'') {
return false;
}
// null != {}
if (a instanceof Object != b instanceof Object) {
return false;
}
if (a instanceof Date || b instanceof Date) {
if (a instanceof Date != b instanceof Date ||
a.getTime() != b.getTime()) {
return false;
}
}
var allKeys = [].concat(keys(a), keys(b));
uniqueArray(allKeys);
for (var i = 0; i < allKeys.length; i++) {
var prop = allKeys[i];
if (!isEqual(a[prop], b[prop])) {
return false;
}
}
return true;
}
Código fuente (incluidas las funciones de ayuda, generalType y uniqueArray): Unit Test y Test Runner aquí .
Esta es una adición para todo lo anterior, no un reemplazo. Si necesita comparar rápidamente objetos de poca profundidad sin necesidad de verificar casos recursivos adicionales. Aquí hay un tiro.
Esto se compara con: 1) Igualdad de número de propiedades propias, 2) Igualdad de nombres clave, 3) si bCompareValues == verdadero, Igualdad de los valores de propiedad correspondientes y sus tipos (triple igualdad)
var shallowCompareObjects = function(o1, o2, bCompareValues) {
var s,
n1 = 0,
n2 = 0,
b = true;
for (s in o1) { n1 ++; }
for (s in o2) {
if (!o1.hasOwnProperty(s)) {
b = false;
break;
}
if (bCompareValues && o1[s] !== o2[s]) {
b = false;
break;
}
n2 ++;
}
return b && n1 == n2;
}
Sé que es un poco viejo, pero me gustaría agregar una solución que se me ocurrió para este problema. Tenía un objeto y quería saber cuándo cambiaron sus datos. "algo similar a Object.observe" y lo que hice fue:
function checkObjects(obj,obj2){
var values = [];
var keys = [];
keys = Object.keys(obj);
keys.forEach(function(key){
values.push(key);
});
var values2 = [];
var keys2 = [];
keys2 = Object.keys(obj2);
keys2.forEach(function(key){
values2.push(key);
});
return (values == values2 && keys == keys2)
}
Aquí se puede duplicar y crear otro conjunto de arreglos para comparar los valores y las claves. Es muy simple porque ahora son matrices y devolverán falso si los objetos tienen tamaños diferentes.