Domanda

Un rigoroso operatore per l'uguaglianza ti dirà se due tipi di oggetto sono uguali. Tuttavia, c'è un modo per dire se due oggetti sono uguali, molto simile al valore del codice hash in Java?

Domanda Stack Overflow Esiste qualche tipo di funzione hashCode in JavaScript? è simile a questa domanda , ma richiede una risposta più accademica. Lo scenario sopra mostra perché sarebbe necessario averne uno e mi chiedo se esiste una soluzione equivalente .

È stato utile?

Soluzione

La risposta breve

La semplice risposta è: No, non esistono mezzi generici per determinare se un oggetto è uguale a un altro nel senso che intendi. L'eccezione è quando stai pensando rigorosamente a un oggetto che non è tipizzato.

La risposta lunga

Il concetto è quello di un metodo Equals che confronta due diverse istanze di un oggetto per indicare se sono uguali a livello di valore. Tuttavia, spetta al tipo specifico definire come deve essere implementato un metodo Equals . Un confronto iterativo di attributi che hanno valori primitivi potrebbe non essere sufficiente, potrebbero esserci degli attributi che non devono essere considerati parte del valore dell'oggetto. Ad esempio,

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

In questo caso, c non è davvero importante per determinare se due istanze di MyClass sono uguali, solo a e b sono importante. In alcuni casi c potrebbe variare tra istanze e tuttavia non essere significativo durante il confronto.

Nota che questo problema si applica quando i membri stessi possono essere anche istanze di un tipo e ognuno di loro dovrebbe avere tutti i mezzi per determinare l'uguaglianza.

Ulteriori complicazioni sono che in JavaScript la distinzione tra dati e metodo è sfocata.

Un oggetto può fare riferimento a un metodo che deve essere chiamato come gestore di eventi e questo probabilmente non verrebbe considerato parte del suo "stato del valore". Considerando che a un altro oggetto può essere assegnata una funzione che esegue un calcolo importante e rende quindi questa istanza diversa dalle altre semplicemente perché fa riferimento a una funzione diversa.

Che dire di un oggetto che ha uno dei suoi metodi prototipo esistenti sovrascritto da un'altra funzione? Potrebbe ancora essere considerato uguale a un'altra istanza che altrimenti identico? È possibile rispondere a questa domanda solo in ciascun caso specifico per ciascun tipo.

Come affermato in precedenza, l'eccezione sarebbe un oggetto rigorosamente non tipizzato. Nel qual caso l'unica scelta sensata è un confronto iterativo e ricorsivo di ciascun membro. Anche allora bisogna chiedersi qual è il "valore" di una funzione?

Altri suggerimenti

Perché reinventare la ruota? Prova Lodash . Ha una serie di funzioni indispensabili come isEqual () .

_.isEqual(object, other);

Controllerà con forza ogni valore chiave - proprio come gli altri esempi in questa pagina - usando ECMAScript & nbsp ; 5 e ottimizzazioni native se sono disponibili nel browser.

Nota: in precedenza questa risposta raccomandava Underscore.js , ma lodash ha svolto un lavoro migliore nel correggere i bug e nell'affrontare i problemi con coerenza.

L'operatore di uguaglianza predefinito in JavaScript per gli oggetti restituisce true quando si riferiscono alla stessa posizione in memoria.

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

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

Se hai bisogno di un operatore di uguaglianza diverso dovrai aggiungere un metodo uguale (altro) o qualcosa di simile alle tue classi e le specifiche del tuo dominio problematico determineranno esattamente cosa significa .

Ecco un esempio di carta da gioco:

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 lavori in AngularJS , la funzione angular.equals determinerà se due oggetti sono uguali. In Ember.js usa isEqual .

  • angular.equals - Consulta i docs o < a href = "https://github.com/angular/angular.js/blob/6c59e770084912d2345e7f83f983092a2d305ae3/src/Angular.js#L670" rel = "noreferrer"> source per ulteriori informazioni su questo metodo. Fa un confronto profondo anche sugli array.
  • Ember.js isEqual - Vedi docs o source per ulteriori informazioni su questo metodo . Non fa un confronto profondo sugli array.

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>

Questa è la mia versione. Sta usando la nuova Object.keys che è stato introdotto in ES5 e idee / test da + , + 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 si utilizza una libreria JSON, è possibile codificare ciascun oggetto come JSON, quindi confrontare le stringhe risultanti per l'uguaglianza.

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

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

NOTA: sebbene questa risposta funzionerà in molti casi, come diverse persone hanno sottolineato nei commenti, è problematica per una serie di motivi. In quasi tutti i casi ti consigliamo di trovare una soluzione più solida.

Implementazione funzionale deepEqual breve:

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

Modifica : versione 2, usando il suggerimento del braccio e le funzioni della freccia 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);
}

Se hai una funzione di copia profonda a portata di mano, puoi usare il seguente trucco per ancora usare JSON.stringify mentre combini l'ordine delle proprietà:

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

Demo: http://jsfiddle.net/CU3vb/3/

Motivazione:

Poiché le proprietà di obj1 vengono copiate sul clone una alla volta, il loro ordine nel clone verrà conservato. E quando le proprietà di obj2 vengono copiate nel clone, poiché le proprietà già esistenti in obj1 verranno semplicemente sovrascritte, i loro ordini nel clone verranno conservati.

Stai provando a verificare se due oggetti sono uguali? cioè: le loro proprietà sono uguali?

In questo caso, probabilmente avrai notato questa situazione:

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

potresti dover fare qualcosa del genere:

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

Ovviamente quella funzione potrebbe avere un bel po 'di ottimizzazione e la possibilità di fare un controllo approfondito (per gestire oggetti nidificati: var a = {foo: {fu: " bar "}} ) ma hai avuto l'idea.

Come sottolineato da FOR, potresti doverlo adattare ai tuoi scopi, ad esempio: classi diverse possono avere definizioni diverse di "uguale". Se stai solo lavorando con oggetti semplici, quanto sopra potrebbe essere sufficiente, altrimenti una funzione personalizzata MyClass.equals () potrebbe essere la strada da percorrere.

Soluzioni

?? più semplici e logiche per confrontare qualsiasi cosa come Object, Array, String, Int ...

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

Nota:

  • devi sostituire val1 e val2 con il tuo oggetto
  • per l'oggetto, devi ordinare (per chiave) in modo ricorsivo per entrambi gli oggetti laterali

In Node.js, puoi usare il suo native (" assert "). deepEqual . Ulteriori informazioni: http://nodejs.org/api/assert.html

Ad esempio:

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

Un altro esempio che restituisce true / false invece di restituire errori:

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

Uso questa funzione confrontabile per produrre copie dei miei oggetti che sono comparabili a 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>

È utile nei test (la maggior parte dei framework di test ha una funzione is ). Per es.

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

Se viene rilevata una differenza, le stringhe vengono registrate, rendendo le differenze compilabili:

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

Ecco una soluzione in ES6 / ES2015 usando un approccio di tipo funzionale:

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 disponibile qui

Non so se qualcuno abbia pubblicato qualcosa di simile a questo, ma ecco una funzione che ho fatto per verificare la parità degli oggetti.

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

Inoltre, è ricorsivo, quindi può anche verificare la profonda uguaglianza, se è così che lo chiami.

puoi usare _.isEqual (obj1, obj2) dalla libreria underscore.js.

Ecco un esempio:

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

Consulta la documentazione ufficiale da qui: http://underscorejs.org/#isEqual

Una semplice soluzione a questo problema che molte persone non capiscono è quella di ordinare le stringhe JSON (per carattere). Questo di solito è anche più veloce delle altre soluzioni menzionate qui:

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

Un'altra cosa utile di questo metodo è che puoi filtrare i confronti passando un "rimpiazzo" funzione per le funzioni JSON.stringify ( https: // developer. mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter ). Quanto segue confronterà solo tutte le chiavi degli oggetti che sono denominate " 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;
});

Vorrei sconsigliare l'hashing o la serializzazione (come suggerisce la soluzione JSON). Se è necessario verificare se due oggetti sono uguali, è necessario definire cosa significa uguale. È possibile che tutti i membri dei dati in entrambi gli oggetti corrispondano o che le posizioni di memoria debbano corrispondere (nel senso che entrambe le variabili fanno riferimento allo stesso oggetto in memoria) oppure che deve corrispondere solo un membro dei dati in ciascun oggetto.

Di recente ho sviluppato un oggetto il cui costruttore crea un nuovo ID (a partire da 1 e incrementando di 1) ogni volta che viene creata un'istanza. Questo oggetto ha una funzione isEqual che confronta quel valore id con il valore id di un altro oggetto e restituisce true se corrispondono.

In quel caso ho definito "uguale" come significato i valori dell'id corrispondono. Dato che ogni istanza ha un ID univoco, questo potrebbe essere usato per rafforzare l'idea che gli oggetti corrispondenti occupino anche la stessa posizione di memoria. Sebbene ciò non sia necessario.

Avendo bisogno di una funzione di confronto degli oggetti più generica di quanto fosse stato pubblicato, ho elaborato quanto segue. Critica apprezzata ...

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 si confrontano oggetti JSON è possibile utilizzare https://github.com/mirek/node -rus-diff

npm install rus-diff

Utilizzo:

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 due oggetti sono diversi, un compatibile con MongoDB <$> {$ rename: {...}, $ unset: {...}, $ set: {...}} come l'oggetto è restituito.

Ho affrontato lo stesso problema e ho deciso di scrivere la mia soluzione. Ma poiché voglio anche confrontare le matrici con gli oggetti e viceversa, ho creato una soluzione generica. Ho deciso di aggiungere le funzioni al prototipo, ma è possibile riscriverle facilmente in funzioni autonome. Ecco il codice:

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

Questo algoritmo è diviso in due parti; La funzione equals stessa e una funzione per trovare l'indice numerico di una proprietà in un array / oggetto. La funzione find è necessaria solo perché indexof trova solo numeri e stringhe e nessun oggetto.

Uno può chiamarlo così:

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

La funzione restituisce true o false, in questo caso true. L'algoritmo als consente il confronto tra oggetti molto complessi:

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

L'esempio superiore restituirà vero, anche se le proprietà hanno un ordine diverso. Un piccolo dettaglio da tenere in considerazione: questo codice controlla anche lo stesso tipo di due variabili, quindi "3" non è uguale a 3.

Volevo solo contribuire con la mia versione del confronto di oggetti utilizzando alcune funzionalità es6. Non tiene conto di un ordine. Dopo aver convertito tutti gli if / else in ternary sono arrivato con il seguente:

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;

        }
    )
}

È utile considerare due oggetti uguali se hanno tutti gli stessi valori per tutte le proprietà e ricorsivamente per tutti gli oggetti e le matrici nidificati. Considero uguali i seguenti due oggetti:

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

Allo stesso modo, le matrici possono avere "manca". elementi ed elementi indefiniti. Tratterei anche quelli allo stesso modo:

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

Una funzione che implementa questa definizione di uguaglianza:

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

Codice sorgente (comprese le funzioni di supporto, generalType e uniqueArray): Unit Test e Test Runner qui .

Con questa funzione sto formulando i seguenti presupposti:

  1. Controlli gli oggetti che stai confrontando e hai solo valori primitivi (ad es. oggetti non nidificati, funzioni, ecc.).
  2. Il tuo browser ha il supporto per Object.keys .

Questo dovrebbe essere trattato come una dimostrazione di una strategia semplice.

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

Questa è un'aggiunta per tutto quanto sopra, non una sostituzione. Se è necessario eseguire il confronto veloce di oggetti poco profondi senza dover controllare casi ricorsivi aggiuntivi. Ecco uno scatto.

Questo confronta per: 1) Uguaglianza del numero di proprietà proprie, 2) Uguaglianza dei nomi delle chiavi, 3) se bCompareValues ??== true, Uguaglianza dei valori delle proprietà corrispondenti e dei loro tipi (tripla uguaglianza)

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

Per confrontare le chiavi per semplici istanze di oggetti coppia chiave / valore, 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);
};

Dopo aver confrontato le chiavi, è sufficiente un semplice ciclo for..in aggiuntivo.

La complessità è O (N * N) con N è il numero di chiavi.

Spero / suppongo che gli oggetti che definisco non contengano più di 1000 proprietà ...

So che è un po 'vecchio, ma vorrei aggiungere una soluzione che mi è venuta in mente per questo problema. Avevo un oggetto e volevo sapere quando i suoi dati sono cambiati. " qualcosa di simile a Object.observe " e quello che ho fatto è stato:

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

Questo qui può essere duplicato e creare un altro set di matrici per confrontare i valori e le chiavi. È molto semplice perché ora sono array e restituiranno false se gli oggetti hanno dimensioni diverse.

Estrarre dalla mia biblioteca personale, che uso ripetutamente per il mio lavoro. La seguente funzione è un lenient recursive deep equal, che non controlla

  • Uguaglianza di classe
  • Valori ereditati
  • Valori l'uguaglianza rigorosa

Lo uso principalmente per verificare se ricevo risposte uguali contro varie implementazioni API. Dove possono verificarsi differenze di implementazione (come stringa vs numero) e valori null aggiuntivi.

La sua implementazione è piuttosto semplice e breve (se tutti i commenti vengono rimossi)

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

Ecco una versione del trucco stringify che è meno digitante e funziona in molti casi per banali confronti di dati JSON.

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

Vedo le risposte del codice spaghetti. Senza usare librerie di terze parti, questo è molto semplice.

In primo luogo ordinare i due oggetti in base alla chiave dei nomi delle chiavi.

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)

Quindi usa semplicemente una stringa per confrontarli.

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

Per quelli di voi che usano NodeJS, esiste un metodo conveniente chiamato isDeepStrictEqual sulla libreria Util nativa che può raggiungere questo obiettivo

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_isdeepstricte2_val

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top