Comment déterminer l'égalité pour deux objets JavaScript?

un opérateur d'égalité strict vous dira si deux objets types sont égaux. Cependant, y a-t-il un moyen de dire si deux objets sont égaux, un peu comme le code de hachage valeur en Java?

Stack Overflow question 151980920 " y a-t-il une fonction de hashCode dans JavaScript? est similaire à cette question, mais nécessite une réponse plus académique. Le scénario ci-dessus montre pourquoi il serait nécessaire d'en avoir un, et je me demande s'il y a une solution équivalente .

488
demandé sur Community 2008-10-14 17:41:07

30 réponses

La réponse courte

la réponse est simple: non, il n'y a pas de moyen générique pour déterminer qu'un objet est égal à un autre dans le sens que vous voulez dire. L'exception est quand vous pensez strictement à un objet sans caractère.

La réponse longue

le concept est celui D'une méthode égale qui compare deux instances différentes d'un objet pour indiquer si elles sont égales à une valeur. Toutefois, il appartient au type spécifique de définir comment une méthode Equals doit être mise en œuvre. Une comparaison itérative des attributs qui ont des valeurs primitives peut ne pas être suffisante, il peut bien y avoir des attributs qui ne doivent pas être considérés comme faisant partie de la valeur de l'objet. Par exemple,

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

dans ce cas, c n'est pas vraiment important pour déterminer si deux instances de MyClass sont égales, seulement a et b sont importants. Dans certains cas, c peut varier d'un cas à l'autre sans être significatif lors de la comparaison.

Note ce problème s'applique lorsque les membres peuvent eux aussi être des instances d'un type et chaque seraient tous être nécessaire d'avoir un moyen de déterminer l'égalité.

ce qui complique encore les choses, C'est qu'en JavaScript la distinction entre données et méthode est floue.

un objet peut faire référence à méthode qui doit être appelée comme gestionnaire d'événements, et qui ne serait probablement pas considérée comme faisant partie de son "état de valeur". Alors qu'un autre objet peut très bien se voir attribuer une fonction qui effectue un calcul important et rend ainsi cette instance différente des autres simplement parce qu'elle renvoie à une fonction différente.

Qu'en est-il d'un objet dont l'une des méthodes prototypes existantes est supplantée par une autre fonction? Pourrait-il encore être considéré comme égal à une autre instance qu'il identique? On ne peut répondre à cette question que dans chaque cas particulier pour chaque type.

comme indiqué plus haut, l'exception serait un objet strictement sans caractère. Dans ce cas, le seul choix judicieux est une comparaison itérative et récursive de chaque membre. Même alors, on doit se demander quelle est la "valeur" d'une fonction?

143
répondu AnthonyWJones 2014-08-09 13:53:05

Pourquoi réinventer la roue? Donner Lodash un essai. Il a un certain nombre de fonctions obligatoires telles que isEqual() .

_.isEqual(object, other);

il va forcer vérifier chaque valeur clé - comme les autres exemples sur cette page - en utilisant ECMAScript 5 et optimisations natives si elles sont disponibles dans le navigateur.

Note: auparavant, cette réponse recommandait soulignement.js , mais lodash a fait un meilleur travail de correction de bogues et de résolution de problèmes avec cohérence.

422
répondu CoolAJ86 2016-04-25 18:40:55

l'opérateur d'égalité par défaut en JavaScript pour les objets fournit true quand ils se réfèrent au même endroit dans la mémoire.

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

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

si vous avez besoin d'un opérateur d'égalité différent, vous aurez besoin d'ajouter une méthode equals(other) , ou quelque chose comme ça à vos classes et les spécificités de votre domaine de problèmes détermineront ce que cela signifie exactement.

voici un exemple de carte à jouer:

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
138
répondu Daniel X Moore 2010-07-04 22:00:49

si vous travaillez dans AngularJS , la fonction angular.equals déterminera si deux objets sont égaux. Dans De Braise.js utiliser isEqual .

  • angular.equals - voir la docs ou source pour plus de détails sur cette méthode. Il fait une comparaison profonde sur les tableaux aussi.
  • de Braise.js isEqual - voir le docs ou source pour plus sur cette méthode. Il ne fait pas une comparaison profonde sur les tableaux.

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>
68
répondu Troy Harvey 2016-09-08 03:49:58

C'est ma version. Il utilise un nouvel objet .touches caractéristique qui est introduit dans ES5 et idées / tests de + , + et + :

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 } }));
50
répondu Ebrahim Byagowi 2017-05-23 10:31:38

si vous utilisez une bibliothèque JSON, vous pouvez encoder chaque objet en JSON, puis comparer les chaînes résultantes pour l'égalité.

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

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

NOTE: Bien que cette réponse fonctionne dans de nombreux cas, comme plusieurs personnes l'ont souligné dans les commentaires, elle est problématique pour diverses raisons. Dans à peu près tous les cas, vous voudrez trouver une solution plus robuste.

44
répondu Joel Anair 2013-07-01 13:09:40

Court fonctionnelle deepEqual mise en œuvre:

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

Modifier : la version 2, foc à l'aide de la suggestion et de l'ES6 flèche fonctions:

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);
}
21
répondu atmin 2017-02-05 17:49:38

si vous avez une fonction de copie profonde à portée de main, vous pouvez utiliser l'astuce suivante pour still utiliser JSON.stringify tout en respectant l'ordre des propriétés:

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

Démo: http://jsfiddle.net/CU3vb/3 /

Justification:

puisque les propriétés de obj1 sont copiées un par un au clone, leur ordre dans le clone sera préservé. Et quand les propriétés de obj2 sont copiés sur le clone, puisque les propriétés déjà existantes dans obj1 sera simplement remplacé, leurs ordres dans le clone sera préservé.

20
répondu Ates Goral 2012-06-26 15:37:21

Dans Le Noeud.js, vous pouvez utiliser son natif require("assert").deepEqual . Plus d'infos: http://nodejs.org/api/assert.html

par exemple:

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

un autre exemple qui retourne true / false au lieu de retourner les erreurs:

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;
};
16
répondu Rafael Xavier 2014-09-24 12:41:02

essayez-vous de tester si deux objets sont égaux? c'est à dire: leurs propriétés sont égaux?

Si c'est le cas, vous aurez probablement remarqué cette situation:

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

vous pourriez avoir à faire quelque chose comme ceci:

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

évidemment cette fonction pourrait faire avec un peu d'optimisation, et la capacité de faire un contrôle en profondeur (pour manipuler des objets emboîtés: var a = { foo : { fu : "bar" } } ) mais vous obtenez l'idée.

comme pour souligné, vous pourriez avoir à adapter ceci pour vos propres fins, par exemple: différentes classes peuvent avoir différentes définitions de"égal". Si vous travaillez avec des objets simples, ce qui précède peut suffire, sinon une fonction MyClass.equals() personnalisée peut être la solution.

15
répondu nickf 2008-10-14 13:59:48

"151940920 plus" Simple et logique solutions pour comparer tout Comme Object, Array, String, Int...

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

Note:

  • vous devez remplacer val1 et val2 par votre objet
  • pour l'objet, vous devez Trier(par touche) de façon récursive pour les deux objets latéraux
14
répondu Pratik Bhalodiya 2018-05-19 06:01:28

j'utilise cette fonction comparable pour produire des copies de mes objets qui sont comparables à JSON:

var comparable = o => (typeof o != 'object' || !o)? o :
  Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});

// Demo:

var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };

console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>

est pratique dans les tests (la plupart des cadres de test ont une fonction is ). Par exemple:

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

si une différence est prise, les chaînes sont enregistrées, ce qui rend les différences repérables:

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}}.
10
répondu jib 2016-09-05 13:08:26

Heres une solution dans l'ES6/ES2015 à l'aide d'une fonctionnelle approche de style:

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

démo disponible ici

9
répondu Alan R. Soares 2017-02-06 22:30:38

une solution simple à ce problème que beaucoup de gens ne réalisent pas est de trier les chaînes JSON (par caractère). C'est aussi généralement plus rapide que les autres solutions mentionnées ici:

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

une autre chose utile au sujet de cette méthode est que vous pouvez filtrer des comparaisons en passant une fonction de" remplacement " au JSON.fonctions de stringify ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter ). Ce qui suit comparera seulement toutes les clés d'objets qui s'appellent "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;
});
6
répondu th317erd 2015-02-10 18:39:37

vous pouvez utiliser _.isEqual(obj1, obj2) à partir du trait de soulignement.bibliothèque js.

voici un exemple:

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

voir la documentation officielle d'ici: http://underscorejs.org/#isEqual

5
répondu Bentaiba Miled Basma 2018-03-20 18:28:18

si vous utilisez ES6+ via Babel ou autrement, vous pouvez également utiliser Object.is(x, y) .

référence: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y

4
répondu Daniel Li 2015-06-27 20:48:40

Je ne sais pas si quelqu'un a posté quelque chose de similaire à ceci, mais voici une fonction que j'ai faite pour vérifier les égalités d'objet.

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

aussi, c'est récursif, donc ça peut aussi vérifier une égalité profonde, si c'est ce que vous appelez ça.

4
répondu Zac 2017-03-05 17:13:25

je déconseille le hachage ou la sérialisation (comme le suggère la solution JSON). Si vous devez tester si deux objets sont égaux, alors vous devez définir ce que signifie égal. Il se peut que tous les membres de données dans les deux objets concordent, ou que les emplacements de mémoire doivent concorder (ce qui signifie que les deux variables renvoient au même objet dans la mémoire), ou peut-être qu'un seul membre de données dans chaque objet doit concorder.

récemment j'ai développé un objet dont le constructeur crée un nouvel id (à partir de 1 et en incrémentant de 1) chaque fois qu'une instance est créée. Cet objet a une fonction isEqual qui compare cette valeur d'id avec la valeur d'id d'un autre objet et renvoie true s'ils correspondent.

dans ce cas, j'ai défini" égal " comme signifiant que les valeurs de l'id correspondent. Étant donné que chaque instance a un id unique, cela pourrait être utilisé pour renforcer l'idée que les objets appariés occupent également le même emplacement mémoire. Bien que ce n'est pas nécessaire.

3
répondu Bernard Igiri 2008-11-19 16:20:00

ayant besoin d'une fonction de comparaison d'objets plus générique que ce qui avait été affiché, j'ai préparé ce qui suit. Critique apprécié...

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;
}
3
répondu Liam 2010-05-27 12:00:59

si vous comparez des objets JSON, vous pouvez utiliser https://github.com/mirek/node-rus-diff

npm install rus-diff

Utilisation:

a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}

var rusDiff = require('rus-diff').rusDiff

console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

si deux objets sont différents, un {$rename:{...}, $unset:{...}, $set:{...}} compatible MongoDB est retourné.

3
répondu Mirek Rusin 2014-03-11 17:44:49

j'ai fait face au même problème et j'ai décidé d'écrire ma propre solution. Mais parce que je veux aussi comparer des tableaux avec des objets et vice-versa, j'ai conçu une solution générique. J'ai décidé d'ajouter des fonctions pour le prototype, mais on peut facilement les réécrire autonome fonctions. Voici le code:

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

cet algorithme est divisé en deux parties; la fonction égale elle-même et une fonction pour trouver l'index numérique d'une propriété dans un tableau / objet. Le trouver la fonction n'est nécessaire que parce que indexof ne trouve que des nombres et des chaînes et aucun objet .

on peut l'appeler ainsi:

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

la fonction renvoie true ou false, dans ce cas true. L'algorithme als permet la comparaison entre des objets très complexes:

({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'exemple supérieur retournera true, même si les propriétés ont un ordre différent. Un petit détail à surveiller: Ce code vérifie également pour le même type de deux variables, "3" n'est pas le même que 3.

3
répondu Sir_baaron 2016-09-14 07:51:08

il est utile de considérer deux objets égaux s'ils ont toutes les mêmes valeurs pour toutes les propriétés et récursivement pour tous les objets imbriqués et les tableaux. Je considère également les deux objets suivants égaux:

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

de même, les tableaux peuvent comporter des éléments" manquants " et des éléments non définis. Je les traiterais de la même façon:

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

une fonction qui met en œuvre cette définition de l'égalité:

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

code Source ): Test Unitaire et testeur ici .

2
répondu mckoss 2010-08-28 19:52:42

je fais les hypothèses suivantes avec cette fonction:

  1. vous contrôlez les objets que vous comparez et vous n'avez que des valeurs primitives (i.e. objets non imbriqués, fonctions, etc.).
  2. votre navigateur supporte L'objet .les touches .

ceci doit être considéré comme une démonstration d'une stratégie simple.

/**
 * Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
 * @param {Object} object1
 * @param {Object} object2
 * @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
 * @returns {Boolean}
 */
function isEqual( object1, object2, order_matters ) {
    var keys1 = Object.keys(object1),
        keys2 = Object.keys(object2),
        i, key;

    // Test 1: Same number of elements
    if( keys1.length != keys2.length ) {
        return false;
    }

    // If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
    // keys1 = Object.keys({a:2, b:1}) = ["a","b"];
    // keys2 = Object.keys({b:1, a:2}) = ["b","a"];
    // This is why we are sorting keys1 and keys2.
    if( !order_matters ) {
        keys1.sort();
        keys2.sort();
    }

    // Test 2: Same keys
    for( i = 0; i < keys1.length; i++ ) {
        if( keys1[i] != keys2[i] ) {
            return false;
        }
    }

    // Test 3: Values
    for( i = 0; i < keys1.length; i++ ) {
        key = keys1[i];
        if( object1[key] != object2[key] ) {
            return false;
        }
    }

    return true;
}
2
répondu Aldo Fregoso 2013-09-27 01:39:08

il s'agit d'un ajout pour tout ce qui précède, et non d'un remplacement. Si vous avez besoin de fast shallow-comparer des objets sans besoin de vérifier des cas supplémentaires récursifs. Voici une photo.

cette comparaison se compare pour: 1) L'égalité du nombre de propriétés propres, 2) L'égalité des noms clés, 3) si bcarevalues == vrai, L'égalité des valeurs de propriété correspondantes et leurs types (Triple égalité)

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;
}
2
répondu Lex 2013-12-21 01:09:08

pour comparer des clés pour des instances d'objet clé simple/paire de valeurs, j'utilise:

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

une fois les clés comparées, une simple boucle supplémentaire for..in suffit.

la Complexité est O(N*N) avec N est le nombre de clés.

j'espère/je suppose que les objets que je définis ne contiendront pas plus de 1000 propriétés...

2
répondu Hefeust CORTES 2014-08-09 15:03:08

je sais que c'est un peu vieux, mais je voudrais ajouter une solution que j'ai trouvé pour ce problème. J'ai eu un objet et je voulais savoir quand ses données changé. "quelque chose de semblable à l'Objet.observez "et ce que j'ai fait était:

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

cela ici peut être dupliqué et créer un autre ensemble de tableaux pour comparer les valeurs et les clés. Il est très simple parce qu'ils sont maintenant des tableaux et retournera false si les objets ont des tailles différentes.

2
répondu inoabrian 2015-02-11 20:47:58

Tirant de ma bibliothèque personnelle, que j'utilise pour mon travail à plusieurs reprises. La fonction suivante est une douce récursive profonde égale, qui ne vérifie pas

  • l'égalité des classes
  • valeurs héritées
  • Valeurs d'égalité stricte

Je l'utilise principalement pour vérifier si j'obtiens des réponses égales contre diverses implémentations D'API. Lorsque la mise en œuvre diffère (comme la chaîne vs le nombre) et des valeurs nulles supplémentaires, peuvent se produire.

sa mise en œuvre est assez simple et brève (si tous les commentaires sont supprimés)

/** 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!");
	}
})();
2
répondu PicoCreator 2015-04-17 16:06:40

juste voulu contribuer ma version de comparaison d'objets en utilisant certaines fonctionnalités es6. Il ne veut pas prendre une ordonnance en compte. Après avoir converti tous les if / else en ternary je suis venu avec la suivante:

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;

        }
    )
}
2
répondu Egor Litvinchuk 2016-11-15 16:02:49

Voici une version de l'astuce de stringify qui est moins dactylographique et fonctionne dans de nombreux cas pour des comparaisons de données JSON triviales.

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
2
répondu EJW 2018-01-08 22:15:23

il y a une solution très simple pour celui-ci, tout ce que vous avez à faire est JSON.stringify() sur les deux objets lorsque vous comparez les deux.

2
répondu ProgramKiddo 2018-03-30 12:29:11