Pergunta

Um operador de igualdade estrita vai dizer se dois objetos tipos são iguais. No entanto, há uma maneira de saber se dois objetos são iguais, muito parecido com o código hash valor em Java?

pergunta Stack Overflow Existe algum tipo de função hashCode em JavaScript? é semelhante a esta pergunta , mas exige uma resposta mais acadêmico. O cenário acima demonstra por que seria necessário ter um, e eu estou querendo saber se há alguma solução equivalente .

Foi útil?

Solução

A resposta curta

A resposta é simples: Não, não há meios genéricos para determinar que um objeto é igual a outro, no sentido que você quer dizer. A exceção é quando você está estritamente pensar de um ser typeless objeto.

A resposta longa

O conceito é o de um método que compara duas instâncias diferentes de um objeto para indicar se eles são iguais a um nível de valor igual para igual. No entanto, cabe ao tipo específico para definir como um método Equals devem ser implementadas. Uma comparação iterativo de atributos que têm valores primitivos não pode ser suficiente, pode muito bem haver atributos que não são para ser consideradas como parte do valor do objecto. 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 duas instâncias de MyClass são iguais, apenas a a e b são importantes. Em alguns casos c pode variar entre instâncias e ainda não ser significativa durante a comparação.

Nota este problema aplica-se quando os membros serem igualmente instâncias de um tipo e estes cada seriam todos obrigados a ter um meio de determinar a igualdade.

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

Um objeto pode fazer referência a um método que deve ser chamado como um manipulador de eventos, e este provavelmente não será considerada parte de seu 'estado valor'. Considerando outro objeto pode muito bem ser atribuída uma função que executa um cálculo importante e, assim, torna este diferente exemplo dos outros simplesmente porque ele faz referência a uma função diferente.

E sobre um objeto que tem um dos seus métodos de protótipos existentes substituído por outra função? Poderia ainda ser considerada igual a outra instância que de outra forma idêntica? Essa pergunta só pode ser respondida em cada caso específico para cada tipo.

Como afirmado anteriormente, a exceção seria um objeto estritamente typeless. No caso em que a única escolha sensata é uma comparação iterativa e recursiva de cada membro. Mesmo assim, tem de se perguntar o que é o 'valor' de uma função?

Outras dicas

Por que reinventar a roda? Dê Lodash uma tentativa. Tem um número de must-have funções como isEqual () .

_.isEqual(object, other);

Vai força bruta verificar cada valor de chave - assim como os outros exemplos nesta página - usando ECMAScript 5 e nativos otimizações se eles estão disponíveis no navegador.

Nota: Anteriormente esta resposta recomendado Underscore.js , mas lodash tem feito um trabalho melhor de conseguir bugs corrigidos e abordar as questões com consistência.

O operador de igualdade padrão em JavaScript para objetos produz verdadeiro quando eles se referem ao mesmo local na memória.

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

Se você precisar de um operador de igualdade diferente, você precisa adicionar um método equals(other), ou algo parecido às suas aulas e as especificidades do seu domínio do problema irá determinar o que exatamente isso significa.

Aqui está um exemplo do cartão de jogo:

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 em AngularJS , a função angular.equals irá determinar se dois objetos são iguais. Em Ember.js uso isEqual.

  • angular.equals - Veja a docs ou fonte para saber mais sobre este método. Ele faz uma profunda comparar em matrizes também.
  • Ember.js isEqual - Veja a docs ou fonte para saber mais sobre este método. Ele não faz uma profunda comparar em matrizes.

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 é minha versão. Ele está usando novo recurso Object.keys que é introduzido na ES5 e ideias / 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('<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 } }));

Se você estiver usando uma biblioteca JSON, você pode codificar cada objeto como JSON, em seguida, compare as cordas resultantes de igualdade.

var obj1={test:"value"};
var obj2={test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

NOTA: Embora esta resposta vai trabalhar em muitos casos, como várias pessoas têm apontado nos comentários é problemático para uma variedade de razões. Em praticamente todos os casos, você vai querer encontrar uma solução mais robusta.

implementação deepEqual funcional Curto:

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 : versão 2, usando a sugestão de lança e ES6 arrow funções:

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

Se você tem uma função de cópia profunda à mão, você pode usar o seguinte truque para ainda uso JSON.stringify casando a 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/

Justificação:

Uma vez que as propriedades de obj1 são copiados para o clone por um, sua ordem no clone será preservada. E quando as propriedades de obj2 são copiados para o clone, uma vez que as propriedades já existentes no obj1 será simplesmente substituído, suas ordens no clone será preservada.

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

Se este for o caso, você provavelmente vai ter notado 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 como isto:

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 que a função poderia fazer com um pouco de otimização, e a capacidade de fazer a verificação profunda (para lidar com objetos aninhados: var a = { foo : { fu : "bar" } })., Mas você começa a idéia

Quanto aos apontou, você pode ter que adaptar isso para seus próprios fins, como por exemplo: diferentes classes podem ter diferentes definições de "igual". Se você está apenas trabalhando com objetos simples, o acima bastam Maio, caso contrário, uma função MyClass.equals() personalizado pode ser o caminho a percorrer.

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

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

Nota:

  • você precisa substituir val1 val2and com o seu objeto
  • para o objeto, você tem que tipo (por chave) de forma recursiva para ambos os lados objetos

Em Node.js, você pode usar a sua require("assert").deepEqual nativa. 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 ao invés 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;
};

Eu uso essa função comparable para produzir cópias de meus objetos que são JSON comparável:

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>

Vem a calhar em testes (a maioria das estruturas de teste têm uma função is). Por exemplo.

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

Se a diferença for pego, cordas são registrados, tornando diferenças spottable:

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

O Heres uma solução em 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

Eu não sei se alguma coisa postada de ninguém semelhante a este, mas aqui está uma função que fiz para verificar se há igualdades objeto.

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, é recursiva, por isso também pode verificar a existência de profunda igualdade, se é isso que você chamá-lo.

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

Consulte a documentação oficial a partir daqui: http://underscorejs.org/#isEqual

Uma solução simples para este problema que muitas pessoas não percebem é classificar as cordas JSON (por personagem). Este também é geralmente mais rápido do que as outras soluções mencionados 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 este método é que você pode filtrar comparações passando uma função de "substituto" 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 ). A seguir só irá comparar todos os objetos chaves que são nomeados "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 aconselho contra hashing ou serialização (como a solução JSON sugerir). Se você precisa testar se dois objetos são iguais, então você precisa definir o que é igual a meio. Pode ser que todos os membros de dados em ambos os objetos corresponder, ou pode ser que must as posições de memória de jogo (ou seja, ambas as variáveis ??fazem referência ao mesmo objeto na memória), ou pode ser que apenas um membro de dados em cada objeto deve corresponder.

Recentemente eu desenvolvi um objeto cujo construtor cria um novo id (a partir de 1 e incrementando por 1) cada vez que uma instância é criada. Este objeto tem uma função isEqual que compara esse valor id com o valor do ID de outro objeto e retorna verdadeiro se eles combinam.

Nesse caso, eu definido "igual" no sentido de jogo, os valores id. Dado que cada instância tem uma identificação única que poderia ser usada para reforçar a idéia de que os objetos correspondentes também ocupam a mesma posição de memória. Embora isso não é necessário.

Precisando de uma função de comparação objeto mais genérico do que havia sido publicado, eu cozinhei-se o seguinte. Criticar 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ê está comparando objetos 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 objetos são diferentes, uma {$rename:{...}, $unset:{...}, $set:{...}} compatível MongoDB como objeto é retornado.

Eu enfrentei o mesmo problema e deccided para escrever a minha própria solução. Mas porque eu quero Arrays também comparar com objetos e vice-versa, eu trabalhada uma solução genérica. Eu decidi adicionar as funções para o protótipo, mas pode-se facilmente reescrevê-los para funções autônomas. 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; Os iguais funcionar em si e uma função para encontrar o índice numérico de uma propriedade em uma matriz / objeto. A função find só é necessária porque IndexOf só encontra números e strings e nenhum objeto.

Pode-se chamá-lo assim:

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

A função tanto retorna verdadeiro ou falso, neste caso verdadeiro. Os als algoritmo permite a comparação entre objetos 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 tho as propriedades têm uma ordenação diferente. Um pequeno detalhe de olhar para fora:. Este código também verifica o mesmo tipo de duas variáveis, portanto, "3" não é o mesmo que 3

Apenas queria contribuir com a minha versão de comparação objetos utilizando algumas características ES6. Não é preciso ser um fim em conta. Após a conversão de todas as if / else da ternário já vim com seguinte:

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;

        }
    )
}

É útil considerar dois objetos iguais se eles têm todos os mesmos valores para todas as propriedades e de forma recursiva para todos os objetos aninhados e matrizes. Também considero os dois objetos seguintes iguais:

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

Da mesma forma, matrizes podem ter elementos "perdidos" e elementos indefinidos. Gostaria de tratar as pessoas da mesma, bem como:

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

Uma função que implementa esta 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): Unidade de Teste e Test Runner aqui .

Eu estou fazendo as seguintes premissas com esta função:

  1. Você controla os objetos que você está comparando e você só tem valores primitivos (ie. Não objetos aninhadas, funções, etc.).
  2. O seu navegador tem suporte para Object.keys .

Este deve ser tratado como uma demonstração de uma estratégia simples.

/**
 * 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 é uma adição para todo o exposto, não uma substituição. Se você precisar rápido rasa-compare objetos sem necessidade de verificar casos recursiva extras. Aqui é um tiro.

Isto compara para: 1) A igualdade de número de propriedades próprias, 2) Igualdade de nomes-chave, 3) se bCompareValues ??== true, Igualdade de valores de propriedades correspondentes e seus tipos (igualdade triplo)

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 as chaves para a chave simples / valor pares instâncias de objeto, eu 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);
};

Uma vez que as chaves são comparados, um loop for..in adicional simples é suficiente.

A complexidade é O (N * N) com N é o número de teclas.

Espero / palpite objetos defino não irá realizar mais de 1000 propriedades ...

Eu sei que isto é um pouco velho, mas eu gostaria de adicionar uma solução que eu vim com para este problema. Eu tinha um objeto e eu queria saber quando seus dados foram alterados. "Algo semelhante a Object.observe" eo que eu fiz foi:

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

Este aqui pode ser duplicada e criar um outro conjunto de matrizes para comparar os valores e chaves. É muito simples, porque eles já estão matrizes e irá retornar false se os objetos têm tamanhos diferentes.

Retirando da minha biblioteca pessoal, que eu uso para o meu trabalho repetidamente. A função a seguir é uma recursiva branda profunda iguais, o que não verifica

  • Class igualdade
  • valores herdados
  • Valores estrita igualdade

Eu principalmente usar isso para verificar se eu recebo respostas iguais contra vários implementação API. Onde a diferença de implementação (como corda vs número) e valores nulos adicionais, pode ocorrer.

A sua implementação é bastante simples e curto (se todos os comentários é retirado)

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

Aqui está uma versão do truque stringify que é menos digitação e trabalha em muitos casos para comparações de dados JSON triviais.

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }

Eu vejo respostas código espaguete. Sem usar qualquer libs de terceiros, isso é muito fácil.

Em primeiro lugar espécie os dois objetos por chave de seus nomes-chave.

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)

Em seguida, basta usar uma corda para compará-los.

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

Para aqueles de vocês usando NodeJS, há um método conveniente chamado isDeepStrictEqual na biblioteca Util nativa que pode conseguir isso.

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.org/api/util.html#util_util_isdeepstrictequal_val1_val2

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top