Pregunta

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, muy parecido al valor del código hash en Java?

Pregunta de desbordamiento de pila ¿Hay algún tipo de función de código hash 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 .

¿Fue útil?

Solución

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á pensando estrictamente que un objeto no tiene tipo.

La respuesta larga

El concepto es el de un método Equals 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 importante. En algunos casos, c puede variar entre las instancias 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 está 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 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 responderse en cada caso específico para cada tipo.

Como se indicó anteriormente, la excepción sería un objeto estrictamente sin tipo de letra. 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?

Otros consejos

¿Por qué reinventar la rueda? Prueba Lodash . Tiene una serie de funciones imprescindibles, como isEqual () .

_.isEqual(object, other);

Revisará la fuerza bruta cada valor clave, al igual que los otros ejemplos en esta página, utilizando ECMAScript & nbsp ; 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 al corregir los errores y resolver los problemas con coherencia.

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 (otro) , 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 naipe:

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

Si está trabajando en AngularJS , la función angular.equals determinará si dos objetos son iguales En Ember.js use isEqual .

  • angular.equals : consulte docs o < para obtener más información sobre este método. También hace una comparación profunda en las matrices.
  • Ember.js isEqual : consulte docs o

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>

Esta es mi versión. Está utilizando la nueva Object.keys característica que se introduce 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 stackoverflow 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 } }));

Si está utilizando una biblioteca JSON, puede codificar cada objeto como JSON, y 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 varios motivos. En casi todos los casos querrá encontrar una solución más robusta.

Implementación de

Short funcional 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);
}

Editar : 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);
}

Si tiene a mano una función de copia en profundidad, puede usar el siguiente truco para still usar JSON.stringify mientras hace coincidir 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/

Justificación:

Dado que las propiedades de obj1 se copian una a una en el clon, 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.

¿Estás intentando 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 bastante optimización y con la capacidad de hacer una comprobación profunda (para manejar objetos anidados: var a = {foo: {fu: " barra "}} ) pero entiendes la idea.

Como FOR señaló, 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 planos, lo anterior puede ser suficiente, de lo contrario, una función personalizada MyClass.equals () puede ser la forma de hacerlo.

Las soluciones más simples y lógicas para comparar todo, como Objeto, Array, Cadena, Int ...

JSON.stringify ({a: val1}) === JSON.stringify ({a: val2})

Nota:

  • necesita reemplazar val1 y val2 con su objeto
  • para el objeto, debe ordenar (por clave) recursivamente para ambos objetos laterales

En Node.js, puede usar su 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 en 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;
};

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 is ). Por ejemplo,

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 visibles:

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}}.

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;
  }
}

demostración disponible aquí

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 si hay una gran igualdad, si así lo llamas.

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

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 puedes filtrar las comparaciones pasando un " sustituto " función a las funciones 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;
});

Yo recomendaría no hacer hashing o 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 un nuevo ID (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 " como significan los valores de id coinciden Dado que cada instancia tiene un ID único, 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.

Necesitando 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;
}

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, un {$ rename: {...}, $ unset: {...}, $ set: {...}} compatible con MongoDB es devuelto.

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.

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 los 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;

        }
    )
}

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 forma similar, las matrices pueden tener " desaparecidas " Elementos 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): Prueba de unidad y Corredor de pruebas aquí .

Estoy haciendo las siguientes suposiciones con esta función:

  1. Usted controla los objetos que está comparando y solo tiene valores primitivos (es decir, no objetos, funciones, etc. anidados).
  2. Su navegador tiene soporte para Object.keys .

Esto debe tratarse 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;
}

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 de clave, 3) si bCompareValues ??== true, Igualdad de 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;
}

Para comparar claves para instancias de objetos de pares clave / valor simples, 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 tendrán más de 1000 propiedades ...

Sé que esto 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)
}

Esto 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.

Al salir de mi biblioteca personal, que uso para mi trabajo repetidamente. La siguiente función es una indulgencia profunda igual a la 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 breve (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 eugene@picoded.com, 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!");
	}
})();

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 { ... }

Veo respuestas de código de espagueti. Sin usar librerías de terceros, esto es muy fácil.

Primero, ordene los dos objetos por clave sus nombres de clave.

let objectOne = { hey, you }
let objectTwo = { you, hey }

// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
    return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}

let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)

Luego simplemente usa una cadena para compararlas.

JSON.stringify(objectOne) === JSON.stringify(objectTwo)

Para aquellos de ustedes que usan NodeJS, hay un método conveniente llamado isDeepStrictEqual en la biblioteca de Util nativa que puede lograr esto.

const util = require('util');

const foo = {
  hey: "ho",
  lets: "go"
}

const bar = {
  hey: "ho",
  lets: "go"
}

foo == bar // false
util.isDeepStrictEqual(foo, bar) // true

https://nodejs.orgml/api/util.utml#util_util_util_util_util_util_util_pop.ppl.pros.prosoft.png

scroll top