Duplicata possible:
Comment déterminez-vous l'égalité pour deux objets JavaScript?
Comparaison d'objets en JavaScript
Si j'ai deux tableaux ou objets et que je souhaite les comparer, tels que
object1 = [
{ shoes:
[ 'loafer', 'penny' ]
},
{ beers:
[ 'budweiser', 'busch' ]
}
]
object2 = [
{ shoes:
[ 'loafer', 'penny' ]
},
{ beers:
[ 'budweiser', 'busch' ]
}
]
object1 == object2 // false
cela peut être ennuyeux si vous obtenez une réponse d'un serveur et essayez de voir si elle a changé
Mise à jour:
En réponse aux commentaires et aux inquiétudes entourant la suggestion d'origine (en comparant 2 chaînes JSON), vous pouvez utiliser cette fonction:
function compareObjects(o, p)
{
var i,
keysO = Object.keys(o).sort(),
keysP = Object.keys(p).sort();
if (keysO.length !== keysP.length)
return false;//not the same nr of keys
if (keysO.join('') !== keysP.join(''))
return false;//different keys
for (i=0;i<keysO.length;++i)
{
if (o[keysO[i]] instanceof Array)
{
if (!(p[keysO[i]] instanceof Array))
return false;
//if (compareObjects(o[keysO[i]], p[keysO[i]] === false) return false
//would work, too, and perhaps is a better fit, still, this is easy, too
if (p[keysO[i]].sort().join('') !== o[keysO[i]].sort().join(''))
return false;
}
else if (o[keysO[i]] instanceof Date)
{
if (!(p[keysO[i]] instanceof Date))
return false;
if ((''+o[keysO[i]]) !== (''+p[keysO[i]]))
return false;
}
else if (o[keysO[i]] instanceof Function)
{
if (!(p[keysO[i]] instanceof Function))
return false;
//ignore functions, or check them regardless?
}
else if (o[keysO[i]] instanceof Object)
{
if (!(p[keysO[i]] instanceof Object))
return false;
if (o[keysO[i]] === o)
{//self reference?
if (p[keysO[i]] !== p)
return false;
}
else if (compareObjects(o[keysO[i]], p[keysO[i]]) === false)
return false;//WARNING: does not deal with circular refs other than ^^
}
if (o[keysO[i]] !== p[keysO[i]])//change !== to != for loose comparison
return false;//not the same value
}
return true;
}
Mais dans de nombreux cas, il n'est pas nécessaire que l'OMI soit aussi difficile:
JSON.stringify(object1) === JSON.stringify(object2);
Si les objets stringifiés sont identiques, leurs valeurs sont identiques.
Par souci d'exhaustivité: JSON
ignore simplement les fonctions (enfin, les supprime toutes ensemble). Il est censé représenter les données , pas la fonctionnalité .
Si vous essayez de comparer 2 objets qui ne contiennent que des fonctions, vous obtiendrez true
:
JSON.stringify({foo: function(){return 1;}}) === JSON.stringify({foo: function(){ return -1;}});
//evaulutes to:
'{}' === '{}'
//is true, of course
Pour une comparaison approfondie des objets/fonctions, vous devrez vous tourner vers des bibliothèques ou écrire votre propre fonction, et surmonter le fait que les objets JS sont toutes des références, donc lorsque vous comparez o1 === ob2
, Cela ne retournera vrai que si les deux variables pointent vers le même objet ...
Comme @ a-j l'a souligné dans le commentaire:
JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});
est false
, car les deux appels de chaîne donnent respectivement "{"a":1,"b":2}"
et "{"b":2,"a":1}"
. Quant à savoir pourquoi, vous devez comprendre les caractéristiques internes du moteur V8 de Chrome. Je ne suis pas un expert, et sans entrer dans trop de détails, voici ce que cela se résume à:
Chaque objet créé et chaque fois qu'il est modifié, V8 crée une nouvelle classe C++ cachée (en quelque sorte). Si l'objet X a une propriété a
et qu'un autre objet a la même propriété, ces deux objets JS référenceront une classe cachée qui hérite d'une classe cachée partagée qui définit cette propriété a
. Si deux objets partagent tous les mêmes propriétés de base, ils référenceront tous les mêmes classes cachées et JSON.stringify
Fonctionnera exactement de la même manière sur les deux objets. C'est une donnée (Plus de détails sur les internes de V8 ici , si vous êtes intéressé).
Cependant, dans l'exemple souligné par a-j, les deux objets sont stringifiés différemment. Comment venir? Eh bien, tout simplement, ces objets n'existent jamais en même temps:
JSON.stringify({a: 1, b: 2})
Il s'agit d'un appel de fonction, une expression qui doit être résolue à la valeur résultante avant de pouvoir être comparée à l'opérande de droite. Le deuxième objet littéral n'est pas encore sur la table.
L'objet est stratifié et l'exoression est résolue en une constante chaîne. Le littéral objet n'est référencé nulle part et est marqué pour la récupération de place.
Après cela, l'opérande de droite (l'expression JSON.stringify({b: 2, a: 1})
) reçoit le même traitement.
Tout va bien et dandy, mais ce qui doit également être pris en considération, c'est que les moteurs JS sont maintenant beaucoup plus sophistiqués qu'auparavant. Encore une fois, je ne suis pas un expert du V8, mais je pense qu'il est plausible que l'extrait de a-j soit fortement optimisé, dans la mesure où le code est optimisé pour:
"{"b":2,"a":1}" === "{"a":1,"b":2}"
En omettant essentiellement les appels JSON.stringify
Et en ajoutant simplement des guillemets aux bons endroits. C'est, après tout, beaucoup plus efficace.
En tant que mixage de soulignement:
dans le scénario du café:
_.mixin deepEquals: (ar1, ar2) ->
# typeofs should match
return false unless (_.isArray(ar1) and _.isArray(ar2)) or (_.isObject(ar1) and _.isObject(ar2))
#lengths should match
return false if ar1.length != ar2.length
still_matches = true
_fail = -> still_matches = false
_.each ar1, (prop1, n) =>
prop2 = ar2[n]
return if prop1 == prop2
_fail() unless _.deepEquals prop1, prop2
return still_matches
Et en javascript:
_.mixin({
deepEquals: function(ar1, ar2) {
var still_matches, _fail,
_this = this;
if (!((_.isArray(ar1) && _.isArray(ar2)) || (_.isObject(ar1) && _.isObject(ar2)))) {
return false;
}
if (ar1.length !== ar2.length) {
return false;
}
still_matches = true;
_fail = function() {
still_matches = false;
};
_.each(ar1, function(prop1, n) {
var prop2;
prop2 = ar2[n];
if (prop1 !== prop2 && !_.deepEquals(prop1, prop2)) {
_fail();
}
});
return still_matches;
}
});