Question

Un opérateur d'égalité stricte vous dira si deux types d'objets sont égaux. Cependant, y a-t-il un moyen de savoir si deux objets sont égaux, très semblable au code de hachage en Java?

Question relative au débordement de la pile Existe-t-il une fonction 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 existe une solution équivalente .

Était-ce utile?

La solution

La réponse courte

La réponse simple est: non, il n’existe aucun moyen générique de déterminer qu’un objet est égal à un autre au sens où vous l'entendez. L'exception est lorsque vous pensez strictement à un objet non typé.

La réponse longue

Le concept est celui d'une méthode Equals qui compare deux instances différentes d'un objet pour indiquer si elles sont égales au niveau de la valeur. Cependant, il appartient au type spécifique de définir comment une méthode Equals doit être implémentée. Une comparaison itérative d'attributs ayant des valeurs primitives peut ne pas être suffisante, il peut également 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, seuls a et b sont important. Dans certains cas, c peut varier d'une instance à l'autre, sans toutefois être significatif lors de la comparaison.

Notez que ce problème se pose lorsque les membres peuvent être eux-mêmes des instances d'un type et que chacun d'entre eux devrait disposer d'un moyen de déterminer l'égalité.

Ce qui complique encore les choses, c’est que, en JavaScript, la distinction entre données et méthode est floue.

Un objet peut référencer une méthode à appeler en tant que gestionnaire d'événements, ce qui ne serait probablement pas considéré 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, ce qui le rend différent des autres simplement parce qu’il fait référence à une fonction différente.

Qu'en est-il d'un objet dont l'une de ses méthodes de prototype existantes est remplacée par une autre fonction? Pouvait-il toujours être considéré comme égal à un autre cas qui serait autrement identique? Vous ne pouvez répondre à cette question que dans chaque cas spécifique et pour chaque type.

Comme indiqué précédemment, l'exception serait un objet strictement non typé. Dans ce cas, le seul choix sensé est une comparaison itérative et récursive de chaque membre. Même alors, il faut se demander quelle est la "valeur" d'une fonction?

Autres conseils

Pourquoi réinventer la roue? Essayez Lodash . Il comporte un certain nombre de fonctions indispensables telles que isEqual () .

_.isEqual(object, other);

La force brute vérifiera chaque valeur de clé - comme les autres exemples de cette page - en utilisant ECMAScript & nbsp ; 5 et optimisations natives si elles sont disponibles dans le navigateur.

Remarque: cette réponse recommandait auparavant Underscore.js , mais lodash a mieux fait de corriger les bogues et de résoudre les problèmes de cohérence.

L'opérateur d'égalité par défaut dans JavaScript for Objects renvoie true lorsqu'il se réfère au même emplacement en mémoire.

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

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

Si vous avez besoin d'un autre opérateur d'égalité, vous devez ajouter une méthode égale à (autre) , ou quelque chose de similaire, dans vos classes. Les détails de votre domaine problématique 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

Si vous travaillez dans AngularJS , la fonction angular.equals déterminera si deux objets sont égaux. Dans Ember.js , utilisez isEqual .

  • angular.equals - Consultez le docs ou < a href = "https://github.com/angular/angular.js/blob/6c59e770084912d2345e7f83309892a2d305ae3/src/Angular.js#L670" rel = "noreferrer"> source pour en savoir plus sur cette méthode. Il effectue également une comparaison approfondie sur les tableaux.
  • Ember.js isEqual - Consultez le docs ou source pour en savoir plus sur cette méthode . Il ne fait pas de 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>

Ceci est ma version. Il utilise la nouvelle Object.keys qui est introduit dans ES5 et idées / tests à partir 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 } }));

Si vous utilisez une bibliothèque JSON, vous pouvez coder chaque objet au format JSON, puis comparer les chaînes obtenues pour obtenir l'égalité.

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

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

REMARQUE: Bien que cette réponse fonctionne dans de nombreux cas, comme plusieurs personnes l'ont souligné dans les commentaires, cela pose problème pour diverses raisons. Dans presque tous les cas, vous voudrez trouver une solution plus robuste.

Implémentation fonctionnelle deepEqual fonctionnelle:

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 : version 2, à l'aide des fonctions de suggestion de flèche et de flèche ES6:

function deepEqual(x, y) {
  const ok = Object.keys, tx = typeof x, ty = typeof y;
  return x && y && tx === 'object' && tx === ty ? (
    ok(x).length === ok(y).length &&
      ok(x).every(key => deepEqual(x[key], y[key]))
  ) : (x === y);
}

Si vous disposez d'une fonction de copie en profondeur, vous pouvez utiliser l'astuce suivante pour toujours 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:

Les propriétés de obj1 étant copiées une par une sur le clone, leur ordre dans le clone sera préservé. Et lorsque les propriétés de obj2 sont copiées dans le clone, étant donné que les propriétés déjà existantes dans obj1 seront simplement remplacées, leurs commandes dans le clone seront conservées.

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

Si tel 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 devrez peut-être 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 nécessiter un peu d'optimisation et la possibilité d'effectuer une vérification approfondie (pour gérer les objets imbriqués: var a = {foo: {fu: {fu: "bar"}} ) mais vous avez l’idée.

Comme FOR l'a fait remarquer, vous devrez peut-être l'adapter à vos propres objectifs, par exemple: différentes classes peuvent avoir différentes définitions de "égal". Si vous travaillez uniquement avec des objets simples, ce qui précède peut suffire. Sinon, une fonction personnalisée MyClass.equals () peut s'avérer être la solution.

Les solutions les plus simples et logiques pour tout comparer, comme Objet, Tableau, Chaîne, Int ...

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

Remarque:

  • vous devez remplacer val1 et val2 par votre objet
  • pour l'objet, vous devez trier (par clé) de manière récursive pour les deux objets latéraux

Dans Node.js, vous pouvez utiliser son require natif ("assert"). deepEqual . Plus d'informations: 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 renvoie true / false au lieu de renvoyer des 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;
};

J'utilise cette fonction comparable pour produire des copies de mes objets JSON comparables:

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 dans les tests (la plupart des frameworks de test ont une fonction est ). Ex.

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

Si une différence est détectée, les chaînes sont enregistrées, ce qui permet de différencier les différences:

x must match y
got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.

Heres est une solution ES6 / ES2015 utilisant une approche fonctionnelle:

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

la démo disponible ici

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

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

En outre, il est récursif. Il permet donc de vérifier la profonde égalité, si c'est ce que vous appelez.

vous pouvez utiliser _. isEqual (obj1, obj2) depuis la bibliothèque underscore.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 à partir d'ici: http://underscorejs.org/#isEqual

Une solution simple à ce problème que beaucoup de personnes 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 à propos de cette méthode est que vous pouvez filtrer les comparaisons en transmettant un "remplaçant". fonction aux fonctions JSON.stringify ( https: // developer. mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter ). Ce qui suit ne comparera que toutes les clés d’objets nommées "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;
});

Je vous déconseille le hachage ou la sérialisation (comme le suggère la solution JSON). Si vous devez tester si deux objets sont égaux, vous devez définir ce que signifie "égal". Il se peut que tous les membres de données des deux objets correspondent, ou que les emplacements de mémoire doivent correspondre (ce qui signifie que les deux variables référencent le même objet en mémoire), ou qu’un seul membre de données de chaque objet doit correspondre.

Récemment, j'ai développé un objet dont le constructeur crée un nouvel identifiant (en commençant par 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 id à la valeur id d’un autre objet et renvoie true si elles correspondent.

Dans ce cas, j'ai défini " égal " en ce sens que les valeurs id correspondent. Étant donné que chaque instance a un identifiant unique, ceci pourrait être utilisé pour imposer l’idée que les objets correspondants occupent également le même emplacement mémoire. Bien que ce ne soit pas nécessaire.

Ayant besoin d’une fonction de comparaison d’objets plus générique que celle publiée, j’ai concocté ce qui suit. La critique appréciée ...

Object.prototype.equals = function(iObj) {
  if (this.constructor !== iObj.constructor)
    return false;
  var aMemberCount = 0;
  for (var a in this) {
    if (!this.hasOwnProperty(a))
      continue;
    if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
      return false;
    ++aMemberCount;
  }
  for (var a in iObj)
    if (iObj.hasOwnProperty(a))
      --aMemberCount;
  return aMemberCount ? false : true;
}

Si 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 objet {$ renommer: {...} compatible avec MongoDB, $ non défini: {...}, $ set: {...}} est un objet similaire. retourné.

J'ai rencontré le même problème et j'ai décidé d'écrire ma propre solution. Mais parce que je veux aussi comparer les tableaux avec les objets et vice-versa, j'ai conçu une solution générique. J'ai décidé d'ajouter les fonctions au prototype, mais on peut facilement les réécrire en fonctions autonomes. 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 equals elle-même et une fonction permettant de trouver l'index numérique d'une propriété dans un tableau / objet. La fonction de recherche est uniquement nécessaire car indexof ne trouve que des nombres et des chaînes et aucun objet.

On peut l'appeler comme ça:

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

La fonction renvoie true ou false, dans ce cas, true. L'algorithme permet également de comparer 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 renvoie true, même si les propriétés ont un ordre différent. Un petit détail à surveiller: ce code vérifie également le même type de deux variables, donc "3". n'est pas la même chose que 3.

Je voulais simplement contribuer à ma version de la comparaison d'objets en utilisant certaines fonctionnalités de es6. Il ne prend pas en compte une commande. Après avoir converti tous les if / else en ternary, je suis venu avec ce qui suit:

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;

        }
    )
}

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

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

De même, les tableaux peuvent avoir " manquant " éléments et é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 implémente cette définition d'é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 (y compris les fonctions d'assistance, generalType et uniqueArray): Test unitaire et Testez Runner ici .

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 (c’est-à-dire des objets non imbriqués, des fonctions, etc.).
  2. Votre navigateur prend en charge les Object.keys. .

Cela doit être traité 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;
}

Ceci est un ajout pour tout ce qui précède, pas un remplaçant. Si vous avez besoin de comparer rapidement des objets peu profonds sans vérifier les cas récursifs supplémentaires. Voici un coup de feu.

Ceci se compare pour: 1) l’égalité du nombre de propriétés propres, 2) l’égalité des noms de clé, 3) si bCompareValues ??== true, 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;
}

Pour comparer des clés pour des instances d'objet simples paires paire clé / valeur, 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 pour..en suffit.

La complexité est O (N * N), N étant le nombre de clés.

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

Je sais que c'est un peu vieux, mais je voudrais ajouter une solution que j'ai proposée pour résoudre ce problème. J'avais un objet et je voulais savoir quand ses données ont changé. "quelque chose de similaire à Object.observe". 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)
}

Ceci peut être dupliqué ici et créer un autre ensemble de tableaux pour comparer les valeurs et les clés. C’est très simple car ils sont maintenant des tableaux et renverront false si les objets ont des tailles différentes.

Sortir de ma bibliothèque personnelle, que j’utilise régulièrement pour mon travail. La fonction suivante est une égalité profonde récursive clémente, que ne vérifie pas

  • Égalité de classe
  • Valeurs héritées
  • Valeurs égalité stricte

Je l’utilise principalement pour vérifier si j’obtiens des réponses égales pour diverses implémentations d’API. Où différence d'implémentation (comme chaîne contre nombre) et des valeurs null supplémentaires peuvent se produire.

Sa mise en œuvre est assez simple et courte (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!");
	}
})();

Voici une version de l'astuce stringify moins typée qui 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 { ... }

Je vois des réponses au code spaghetti. Sans utiliser de bibliothèque tierce, c'est très simple.

Triez d’abord les deux objets par clé leurs noms de clé.

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)

Utilisez simplement une chaîne pour les comparer.

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

Pour ceux d'entre vous qui utilisent NodeJS, il existe une méthode pratique appelée isDeepStrictEqual dans la bibliothèque native Util qui peut y parvenir.

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

scroll top