Como determinar a igualdade para dois objects JavaScript?

Um operador de igualdade estrito dirá se dois tipos de object são iguais. No entanto, existe uma maneira de saber se dois objects são iguais, muito parecido com o valor do código hash em Java?

Pergunta sobre estouro de pilha Existe algum tipo de function hashCode em JavaScript? é semelhante a esta pergunta, mas requer uma resposta mais acadêmica. O cenário acima demonstra por que seria necessário ter um, e estou me perguntando se existe alguma solução equivalente .

A resposta curta

A resposta simples é: Não, não há meios genéricos para determinar se um object é igual a outro no sentido que você quer dizer. A exceção é quando você está pensando estritamente que um object é sem tipo.

A longa resposta

O conceito é o de um método Equals que compara duas instâncias diferentes de um object para indicar se elas são iguais em um nível de valor. No entanto, cabe ao tipo específico definir como um método Equals deve ser implementado. Uma comparação iterativa de atributos que possuem valores primitivos pode não ser suficiente, pode haver atributos que não devem ser considerados parte do valor do object. Por exemplo,

  function MyClass(a, b) { var c; this.getCLazy = function() { if (c === undefined) c = a * b // imagine * is really expensive return c; } } 

Neste caso acima, c não é realmente importante para determinar se quaisquer duas instâncias de MyClass são iguais, apenas b são importantes. Em alguns casos, c pode variar entre instâncias e ainda não ser significativo durante a comparação.

Observe que esta questão se aplica quando os membros também podem ser instâncias de um tipo e cada um deles teria que ter um meio de determinar a igualdade.

Para complicar ainda mais as coisas, em JavaScript, a distinção entre dados e método é borrada.

Um object pode fazer referência a um método que deve ser chamado como um manipulador de events, e isso provavelmente não seria considerado parte de seu ‘estado de valor’. Enquanto outro object pode bem ser atribuído a uma function que realiza um cálculo importante e, portanto, torna essa instância diferente das outras simplesmente porque faz referência a uma function diferente.

Que tal um object que tem um dos seus protótipos existentes substituídos por outra function? Poderia ainda ser considerado igual a outra instância que de outra forma seria idêntica? Essa questão só pode ser respondida em cada caso específico para cada tipo.

Como dito anteriormente, a exceção seria um object estritamente sem tipo. Nesse caso, a única escolha sensata é uma comparação iterativa e recursiva de cada membro. Mesmo assim, é preciso perguntar qual é o “valor” de uma function?

Por que reinventar a roda? Dê uma chance a Lodash . Ele possui várias funções obrigatórias , como isEqual () .

 _.isEqual(object, other); 

A força bruta verificará cada valor de chave – assim como os outros exemplos nesta página – usando ECMAScript 5 e otimizações nativas, se estiverem disponíveis no navegador.

Nota: Anteriormente, essa resposta recomendou o Underscore.js , mas a lodash fez um trabalho melhor ao corrigir problemas e resolver problemas com consistência.

O operador de igualdade padrão em JavaScript para Objetos produz true quando se refere ao mesmo local na memory.

 var x = {}; var y = {}; var z = x; x === y; // => false x === z; // => true 

Se você precisar de um operador de igualdade diferente, precisará adicionar um método equals(other) ou algo parecido às suas classs, e as especificações do domínio do problema determinarão exatamente o que isso significa.

Aqui está um exemplo de carta de jogar:

 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 

Se você estiver trabalhando no AngularJS , a function angular.equals determinará se dois objects são iguais. Em Ember.js use isEqual .

  • angular.equals – Veja os documentos ou fonts para mais sobre este método. Ele faz uma comparação profunda em matrizes também.
  • Ember.js isEqual – Veja os documentos ou fonte para mais sobre este método. Não faz uma comparação profunda em matrizes.
 var purple = [{"purple": "drank"}]; var drank = [{"purple": "drank"}]; if(angular.equals(purple, drank)) { document.write('got dat'); } 
  

Esta é minha versão. Ele está usando o novo recurso Object.keys que é introduzido no ES5 e idéias / testes de + , + e + :

 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('
Passed
'); } else { document.write('
Failed
'); } }; 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 } }));

Se você estiver usando uma biblioteca JSON, poderá codificar cada object como JSON e comparar as sequências resultantes para obter igualdade.

 var obj1={test:"value"}; var obj2={test:"value2"}; alert(JSON.encode(obj1)===JSON.encode(obj2)); 

NOTA: Embora essa resposta funcione em muitos casos, como várias pessoas apontaram nos comentários, é problemática por vários motivos. Em praticamente todos os casos, você desejará encontrar uma solução mais robusta.

Se você tiver uma function de cópia profunda à mão, poderá usar o truque a seguir para continuar usando JSON.stringify ao corresponder à ordem das propriedades:

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

Demonstração: http://jsfiddle.net/CU3vb/3/

Fundamentação:

Como as propriedades de obj1 são copiadas para o clone uma por uma, sua ordem no clone será preservada. E quando as propriedades de obj2 são copiadas para o clone, uma vez que as propriedades já existentes em obj1 serão simplesmente sobrescritas, suas ordens no clone serão preservadas.

deepEqual funcional curta do 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); } 

Edit : versão 2, usando a sugestão do jib e as funções da seta 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); } 

Você está tentando testar se dois objects são iguais? ou seja: suas propriedades são iguais?

Se este for o caso, você provavelmente já notou esta situação:

 var a = { foo : "bar" }; var b = { foo : "bar" }; alert (a == b ? "Equal" : "Not equal"); // "Not equal" 

você pode ter que fazer algo assim:

 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, essa function poderia fazer com um pouco de otimização, ea capacidade de fazer a verificação profunda (para manipular objects nesteds: var a = { foo : { fu : "bar" } } ), mas você começa a idéia.

Como FOR apontou, você pode ter que adaptar isso para seus próprios propósitos, por exemplo: classs diferentes podem ter diferentes definições de “igual”. Se você está apenas trabalhando com objects simples, o acima pode ser suficiente, caso contrário, uma function personalizada MyClass.equals() pode ser o caminho a percorrer.

No Node.js, você pode usar o seu native require("assert").deepEqual . Mais informações: http://nodejs.org/api/assert.html

Por exemplo:

 var assert = require("assert"); assert.deepEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError 

Outro exemplo que retorna true / false vez de retornar erros:

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

Soluções mais simples e lógicas para comparar tudo como Object, Array, String, Int …

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

Nota:

  • você precisa replace val1 e val2 com seu object
  • para o object, você tem que classificar (por chave) recursivamente para os dois objects laterais

Eu uso essa function comparable para produzir cópias de meus objects que são comparáveis ​​ao 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))); 
 

Heres é uma solução no ES6 / ES2015 usando uma abordagem 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; } } 

demo disponível aqui

Uma solução simples para esse problema que muitas pessoas não percebem é classificar as strings JSON (por caractere). Isso também é geralmente mais rápido que as outras soluções mencionadas aqui:

 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('')); } 

Outra coisa útil sobre esse método é que você pode filtrar comparações passando uma function “replacer” para as funções JSON.stringify ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON / stringify # Example_of_using_replacer_parameter ). O seguinte apenas compara todas as chaves de objects que são denominadas “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; }); 

Eu não sei se alguém postou algo parecido com isso, mas aqui está uma function que fiz para verificar a igualdade de objects.

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

Além disso, é recursivo, por isso também pode verificar a igualdade profunda, se é assim que você chama.

você pode usar _.isEqual(obj1, obj2) da biblioteca underscore.js.

Aqui está um exemplo:

 var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]}; var clone = {name: 'moe', luckyNumbers: [13, 27, 34]}; stooge == clone; => false _.isEqual(stooge, clone); => true 

Veja a documentação oficial aqui: http://underscorejs.org/#isEqual

Eu aconselharia contra hashing ou serialização (como a solução JSON sugere). Se você precisa testar se dois objects são iguais, então você precisa definir o que significa igual. Pode ser que todos os membros de dados em ambos os objects correspondam ou pode ser que os locais de memory correspondam (o que significa que as duas variables ​​referenciam o mesmo object na memory) ou que apenas um membro de dados em cada object deve corresponder.

Recentemente, desenvolvi um object cujo construtor cria um novo id (iniciando em 1 e incrementando em 1) sempre que uma instância é criada. Esse object tem uma function isEqual que compara esse valor de id com o valor de id de outro object e retorna true se eles corresponderem.

Nesse caso eu defini “igual” como significando que os valores de id combinam. Dado que cada instância possui um id exclusivo, isso pode ser usado para reforçar a idéia de que objects correspondentes também ocupam o mesmo local de memory. Embora isso não seja necessário.

Precisando de uma function de comparação de objects mais genérica do que havia sido postada, preparei o seguinte. 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; } 

Se você estiver comparando objects JSON, você pode 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 } } 

Se dois objects forem diferentes, um MongoDB compatível {$rename:{...}, $unset:{...}, $set:{...}} como object é retornado.

Se você estiver usando o ES6 + através do Babel ou de outra forma, você também pode usar o Object.is(x, y) .

Referência: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y

Eu enfrentei o mesmo problema e decido escrever minha própria solução. Mas como também quero comparar Arrays com Objects e vice-versa, criei uma solução genérica. Decidi adicionar as funções ao protótipo, mas é fácil reescrevê-las para funções independentes. Aqui está o 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 é dividido em duas partes; A function equals em si e uma function para encontrar o índice numérico de uma propriedade em uma matriz / object. A function find só é necessária porque indexof encontra apenas números e strings e nenhum object.

Pode-se chamar assim:

 ({a: 1, b: "h"}).equals({a: 1, b: "h"}); 

A function retorna true ou false, neste caso, true. O algoritmo permite a comparação entre objects muito complexos:

 ({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"]}) 

O exemplo superior retornará true, mesmo que as propriedades tenham uma ordenação diferente. Um pequeno detalhe a ser observado: esse código também verifica o mesmo tipo de duas variables, portanto “3” não é o mesmo que 3.

O método Object.is () determina se dois valores são o mesmo valor.

Sintaxe

 Object.is(value1, value2); 

parameters

value1: o primeiro valor a comparar.

value2: o segundo valor a comparar.

Valor de retorno

Um Booleano indicando se os dois argumentos são ou não o mesmo valor.

Exemplos

 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 

Se você quiser mais informações, leia isto, link

É útil considerar dois objects iguais se eles tiverem todos os mesmos valores para todas as propriedades e recursivamente para todos os objects e matrizes nesteds. Eu também considero os seguintes dois objects iguais:

 var a = {p1: 1}; var b = {p1: 1, p2: undefined}; 

Da mesma forma, as matrizes podem ter elementos “ausentes” e elementos indefinidos. Eu trataria os mesmos da mesma forma:

 var c = [1, 2]; var d = [1, 2, undefined]; 

Uma function que implementa essa definição de igualdade:

 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-fonte (incluindo as funções auxiliares, generalType e uniqueArray): Teste de Unidade e Executor de Teste aqui .

Eu estou fazendo as seguintes suposições com esta function:

  1. You control the objects you are comparing and you only have primitive values (ie. not nested objects, functions, etc.).
  2. Your browser has support for Object.keys .

This should be treated as a demonstration of a simple strategy.

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

This is an addition for all the above, not a replacement. If you need to fast shallow-compare objects without need to check extra recursive cases. Here is a shot.

This compares for: 1) Equality of number of own properties, 2) Equality of key names, 3) if bCompareValues == true, Equality of corresponding property values and their types (triple equality)

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

For comparing keys for simple key/value pairs object instances, I use:

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

Once keys are compared, a simple additional for..in loop is enough.

Complexity is O(N*N) with N is the number of keys.

I hope/guess objects I define won’t hold more than 1000 properties…

I know this is a bit old, but I would like to add a solution that I came up with for this problem. I had an object and I wanted to know when its data changed. “something similar to Object.observe” and what I did was:

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

This here can be duplicated and create an other set of arrays to compare the values and keys. It is very simple because they are now arrays and will return false if objects have different sizes.

Pulling out from my personal library, which i use for my work repeatedly. The following function is a lenient recursive deep equal, which does not check

  • Class equality
  • Inherited values
  • Values strict equality

I mainly use this to check if i get equal replies against various API implementation. Where implementation difference (like string vs number) and additional null values, can occur.

Its implementation is quite straightforward and short (if all the comments is stripped off)

 /** 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= 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!"); } })(); 

Just wanted to contribute my version of objects comparison utilizing some es6 features. It doesn’t take an order into account. After converting all if/else’s to ternary I’ve came with following:

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

Depends on what you mean by equality. And therefore it is up to you, as the developer of the classs, to define their equality.

There’s one case used sometimes, where two instances are considered ‘equal’ if they point to the same location in memory, but that is not always what you want. For instance, if I have a Person class, I might want to consider two Person objects ‘equal’ if they have the same Last Name, First Name, and Social Security Number (even if they point to different locations in memory).

On the other hand, we can’t simply say that two objects are equal if the value of each of their members is the same, since, sometimes, you don’t want that. In other words, for each class, it’s up to the class developer to define what members make up the objects ‘identity’ and develop a proper equality operator (be it via overloading the == operator or an Equals method).

Saying that two objects are equal if they have the same hash is one way out. However you then have to wonder how the hash is calculated for each instance. Going back to the Person example above, we could use this system if the hash was calculated by looking at the values of the First Name, Last Name, and Social Security Number fields. On top of that, we are then relying on the quality of the hashing method (that’s a huge topic on its own, but suffice it to say that not all hashes are created equal, and bad hashing methods can lead to more collisions, which in this case would return false matches).