Comment vérifier si deux tableaux sont égaux avec JavaScript? [dupliquer]
cette question a déjà une réponse ici:
- comment comparer des tableaux en JavaScript? 49 réponses
var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
alert(a == b + "|" + b == c);
démo
Comment puis-je vérifier ces tableaux pour l'égalité et obtenir une méthode qui retourne true
si elles sont l'égalité?
est-ce que jQuery offre une méthode pour cela?
16 réponses
C'est ce que vous devez faire. N'utilisez pas stringify
ni < >
.
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
Option 1
option la plus facile, fonctionne dans presque tous les cas, sauf que null
!= = undefined
mais ils sont tous les deux convertis en représentation JSON null
et considérés comme égaux:
function arraysEqual(a1,a2) {
/* WARNING: arrays must not contain {objects} or behavior may be undefined */
return JSON.stringify(a1)==JSON.stringify(a2);
}
( Cela pourrait ne pas fonctionner si votre tableau contient des objets. si cela fonctionne toujours avec les objets dépend de si l'implémentation JSON trie les clés. Par exemple, le JSON de {1:2,3:4}
peut ou ne peut pas être égal à {3:4,1:2}
; cela dépend de la mise en œuvre, et le spec ne fait aucune garantie quelle qu'elle soit. [Mise à jour 2017: en fait la spécification ES6 garantit maintenant que les clés d'objet seront itérées dans l'ordre de 1) propriétés entières, 2) Propriétés dans l'ordre où elles ont été définies, puis 3) propriétés de symboles dans l'ordre où elles ont été définies. Donc si le JSON.l'implémentation de stringify suit Ceci, des objets égaux (dans le sens === mais pas nécessairement dans le sens==) vont stringify à l'égalité des valeurs. Plus de recherche est nécessaire. Donc je suppose que vous pourriez faire un clone maléfique d'un objet avec des propriétés dans l'ordre inverse, mais je ne peux pas imaginer que cela arrive par accident...] au moins sur Chrome, le JSON.la fonction stringify a tendance à renvoyer les clés dans l'ordre où elles ont été définies (du moins que j'ai remarqué), mais ce comportement est très sujet à changement à tout moment et ne devrait pas être utilisé. si vous choisissez de ne pas utiliser d'objets dans vos listes, ceci devrait fonctionner correctement. Si vous avez des objets dans votre liste qui ont tous un identifiant unique, vous pouvez faire a1.map(function(x)}{return {id:x.uniqueId}})
. Si vous avez des objets arbitraires dans votre liste, vous pouvez lire l'option #2.)
Cela fonctionne pour les tableaux imbriqués.
il est, cependant, un peu inefficace en raison de la surabondance de créer ces cordes et de collecte des ordures.
Option 2
option" plus "propre", que vous pouvez remplacer pour traiter des cas spéciaux (comme les objets réguliers et les objets null/undefined et personnalisés, si vous le souhaitez):
// generally useful functions
function type(x) { // does not work in general, but works on JSONable objects we care about... modify as you see fit
// e.g. type(/asdf/g) --> "[object RegExp]"
return Object.prototype.toString.call(x);
}
function zip(arrays) {
// e.g. zip([[1,2,3],[4,5,6]]) --> [[1,4],[2,5],[3,6]]
return arrays[0].map(function(_,i){
return arrays.map(function(array){return array[i]})
});
}
// helper functions
function allCompareEqual(array) {
// e.g. allCompareEqual([2,2,2,2]) --> true
// does not work with nested arrays or objects
return array.every(function(x){return x==array[0]});
}
function isArray(x){ return type(x)==type([]) }
function getLength(x){ return x.length }
function allTrue(array){ return array.reduce(function(a,b){return a&&b},true) }
// e.g. allTrue([true,true,true,true]) --> true
// or just array.every(function(x){return x});
function allDeepEqual(things) {
// works with nested arrays
if( things.every(isArray) )
return allCompareEqual(things.map(getLength)) // all arrays of same length
&& allTrue(zip(things).map(allDeepEqual)); // elements recursively equal
//else if( this.every(isObject) )
// return {all have exactly same keys, and for
// each key k, allDeepEqual([o1[k],o2[k],...])}
// e.g. ... && allTrue(objectZip(objects).map(allDeepEqual))
//else if( ... )
// extend some more
else
return allCompareEqual(things);
}
Démo:
allDeepEqual([ [], [], [] ])
true
allDeepEqual([ [1], [1], [1] ])
true
allDeepEqual([ [1,2], [1,2] ])
true
allDeepEqual([ [[1,2],[3]], [[1,2],[3]] ])
true
allDeepEqual([ [1,2,3], [1,2,3,4] ])
false
allDeepEqual([ [[1,2],[3]], [[1,2],[],3] ])
false
allDeepEqual([ [[1,2],[3]], [[1],[2,3]] ])
false
allDeepEqual([ [[1,2],3], [1,[2,3]] ])
false
pour utiliser ceci comme une fonction régulière, faire:
function allDeepEqual2() {
return allDeepEqual([].slice.call(arguments));
}
Démo:
allDeepEqual2([[1,2],3], [[1,2],3])
true
Options 3
edit : nous sommes en 2016 et ma réponse précédente, trop compliquée, me dérangeait. Cette implémentation recursive et impérative "recursive programming 101" maintient le code très simple, et échoue en outre le plus tôt possible (ce qui nous donne de l'efficacité). Il ne génère pas non plus d'infrastructures de données éphémères superflues (non pas qu'il y ait quelque chose de mal à la programmation fonctionnelle en général, mais juste la garder propre ici).
si nous voulions appliquer cela à des tableaux de tableaux non vides, nous pourrions faire des séries de barres.réduire (arraysEqual).
C'est sa propre fonction, par opposition à l'utilisation de l'Objet.defineProperties à joindre à la Matrice.prototype, puisque cela échouerait avec une erreur clé si nous réussissions dans une valeur non définie (qui est cependant une bonne décision de conception si vous voulez le faire).
Cela ne répond OPs original question.
function arraysEqual(a,b) {
/*
Array-aware equality checker:
Returns whether arguments a and b are == to each other;
however if they are equal-lengthed arrays, returns whether their
elements are pairwise == to each other recursively under this
definition.
*/
if (a instanceof Array && b instanceof Array) {
if (a.length!=b.length) // assert same length
return false;
for(var i=0; i<a.length; i++) // assert each element equal
if (!arraysEqual(a[i],b[i]))
return false;
return true;
} else {
return a==b; // if not both arrays, should be the same
}
}
exemples:
arraysEqual([[1,2],3], [[1,2],3])
true
arraysEqual([1,2,3], [1,2,3,4])
false
arraysEqual([[1,2],[3]], [[1,2],[],3])
false
arraysEqual([[1,2],[3]], [[1],[2,3]])
false
arraysEqual([[1,2],3], undefined)
false
arraysEqual(undefined, undefined)
true
arraysEqual(1, 2)
false
arraysEqual(null, null)
true
arraysEqual(1, 1)
true
arraysEqual([], 1)
false
arraysEqual([], undefined)
false
arraysEqual([], [])
true
si vous voulez appliquer ceci à des structures de données de type JSON avec des objets js, vous pouvez le faire. Heureusement, nous sommes assurés que toutes les clés d'objets sont uniques, alors itérez au-dessus des propriétés des objets et classez-les par clé, puis affirmez que les deux clés triées-tableau est égal et la valeur-tableau sont égaux, et juste recurse. Nous pouvons étendre ce d'inclure des Cartes (où les touches sont aussi unique.) (Cependant si nous étendons ceci aux ensembles, nous courons dans le problème d'isomorphisme de l'arbre http://logic.pdmi.ras.ru / ~smal/files / smal_jass08_slides.pdf - heureusement, ce n'est pas aussi difficile que l'isomorphisme général du Graphe; il y a en fait un algorithme O(#vertices) pour le résoudre, mais il peut devenir très compliqué de le faire efficacement. Le cas pathologique est si vous avez un ensemble composé de beaucoup d'objets apparemment-indistinguible, mais après une inspection plus poussée certains de ces objets peuvent diffèrent comme vous plonger plus profondément en eux. Vous pouvez également contourner cela en utilisant le hachage pour rejeter presque tous les cas.)
Option 4: (suite de l'édition 2016)
cela devrait fonctionner avec la plupart des objets:
function deepEquals(a,b) {
if (a instanceof Array && b instanceof Array)
return arraysEqual(a,b);
if (Object.getPrototypeOf(a)===Object.prototype && Object.getPrototypeOf(b)===Object.prototype)
return objectsEqual(a,b);
if (a instanceof Map && b instanceof Map)
return mapsEqual(a,b);
if (a instanceof Set && b instanceof Set)
throw "Error: set equality by hashing not implemented."
if ((a instanceof ArrayBuffer || ArrayBuffer.isView(a)) && (b instanceof ArrayBuffer || ArrayBuffer.isView(b)))
return typedArraysEqual(a,b);
return a==b; // see note[1] -- IMPORTANT
}
function arraysEqual(a,b) {
if (a.length!=b.length)
return false;
for(var i=0; i<a.length; i++)
if (!deepEquals(a[i],b[i]))
return false;
return true;
}
function objectsEqual(a,b) {
var aKeys = Object.getOwnPropertyNames(a);
var bKeys = Object.getOwnPropertyNames(b);
if (aKeys.length!=bKeys.length)
return false;
aKeys.sort();
bKeys.sort();
for(var i=0; i<aKeys.length; i++)
if (aKeys[i]!=bKeys[i]) // keys must be strings
return false;
return deepEquals(aKeys.map(k=>a[k]), aKeys.map(k=>b[k]));
}
function mapsEqual(a,b) {
if (a.size!=b.size)
return false;
var aPairs = Array.from(a);
var bPairs = Array.from(b);
aPairs.sort((x,y) => x[0]<y[0]);
bPairs.sort((x,y) => x[0]<y[0]);
for(var i=0; i<a.length; i++)
if (!deepEquals(aPairs[i][0],bPairs[i][0]) || !deepEquals(aPairs[i][1],bPairs[i][1]))
return false;
return true;
}
function typedArraysEqual(a,b) {
a = new Uint8Array(a);
b = new Uint8Array(b);
if (a.length != b.length)
return false;
for(var i=0; i<a.length; i++)
if (a[i]!=b[i])
return false;
return true;
}
Démo (pas testé):
var nineTen = new Float32Array(2);
nineTen[0]=9; nineTen[1]=10;
deepEquals(
[[1,[2,3]], 4, {a:5,b:6}, new Map([['c',7],['d',8]]), nineTen],
[[1,[2,3]], 4, {b:6,a:5}, new Map([['d',8],['c',7]]), nineTen]
)
(note: les Cartes sont es6 dictionnaires. Je ne peux pas dire si ils ont O(1) O(log(N)) performances de recherche, mais dans tous les cas, ils sont "ordonnés" en ce sens qu'ils suivent l'ordre dans lequel des paires de valeurs clés ont été insérées. Toutefois, la sémantique selon laquelle deux cartes doivent être égales si des éléments y sont insérés dans un ordre différent est ambiguë. Je donne ci-dessous un exemple d'implémentation d'un deepEquals qui considère deux cartes égales même si des éléments y ont été insérés dans un ordre différent.)
(note [1]: IMPORTANT: NOTION D'égalité: vous peut-être voulez-vous outrepasser la ligne notée avec une notion coutumière d'Égalité, que vous devrez également changer dans les autres fonctions où qu'elle apparaisse. Par exemple, voulez-vous ou non NaN==NaN? Par défaut, ce n'est pas le cas. Il y a des choses encore plus bizarres comme 0=='0'. Considérez-vous que deux objets sont les mêmes si et seulement s'ils sont le même objet en mémoire? Voir https://stackoverflow.com/a/5447170/711085 . Vous devez documenter la notion d'égalité que vous utilisez. )
vous devriez être en mesure d'étendre le ci-dessus à WeakMaps, WeakSets. Je ne sais pas s'il est logique d'étendre à DataViews. Devrait également pouvoir s'étendre à RegExps probablement, etc.
en l'étendant, vous réalisez que vous faites beaucoup de comparaisons inutiles. C'est là que la fonction type
que j'ai définie plus tôt (solution #2) peut être utile; alors vous pouvez envoyer instantanément. Si cela vaut la peine de la surcharge de (éventuellement? pas bien sûr, comment cela fonctionne sous le capot) chaîne représentant le type est à vous. Vous pouvez alors réécrire le dispatcher, i.e. la fonction deepEquals
, pour être quelque chose comme:
var dispatchTypeEquals = {
number: function(a,b) {...a==b...},
array: function(a,b) {...deepEquals(x,y)...},
...
}
function deepEquals(a,b) {
var typeA = extractType(a);
var typeB = extractType(a);
return typeA==typeB && dispatchTypeEquals[typeA](a,b);
}
jQuery n'a pas de méthode pour comparer des tableaux. Toutefois ,la bibliothèque Underscore (ou la bibliothèque lodash comparable) a une telle méthode: isEqual , et il peut traiter une variété d'autres cas (comme les littérales d'objet) ainsi. Pour s'en tenir à l'exemple fourni:
var a=[1,2,3];
var b=[3,2,1];
var c=new Array(1,2,3);
alert(_.isEqual(a, b) + "|" + _.isEqual(b, c));
par ailleurs: Underscore a beaucoup d'autres méthodes que jQuery est absent ainsi, il est un excellent complément à jQuery.
EDIT: , Comme cela a été souligné dans les commentaires, ci-dessus maintenant ne fonctionne que si les deux tableaux ont leurs éléments dans le même ordre, c'est à dire.:
_.isEqual([1,2,3], [1,2,3]); // true
_.isEqual([1,2,3], [3,2,1]); // false
heureusement Javascript a une méthode intégrée pour résoudre ce problème exact, sort
:
_.isEqual([1,2,3].sort(), [3,2,1].sort()); // true
pour les valeurs primitives comme les nombres et les chaînes, c'est une solution facile:
a = [1,2,3]
b = [3,2,1]
a.sort().toString() == b.sort().toString()
l'appel à sort()
garantira que l'ordre des éléments n'a pas d'importance. L'appel toString()
créera une chaîne avec les valeurs séparées par une virgule pour que les deux chaînes puissent être testées pour l'égalité.
avec JavaScript version 1.6 c'est aussi simple que cela:
Array.prototype.equals = function( array ) {
return this.length == array.length &&
this.every( function(this_i,i) { return this_i == array[i] } )
}
par exemple, [].equals([])
donne true
, tandis que [1,2,3].equals( [1,3,2] )
donne false
.
basé sur Tim James réponse et le commentaire de Fox32, ce qui suit devrait vérifier les nulls, avec la supposition que deux nulls ne sont pas égaux.
function arrays_equal(a,b) { return !!a && !!b && !(a<b || b<a); }
> arrays_equal([1,2,3], [1,3,4])
false
> arrays_equal([1,2,3], [1,2,3])
true
> arrays_equal([1,3,4], [1,2,3])
false
> arrays_equal(null, [1,2,3])
false
> arrays_equal(null, null)
false
même si cela peut sembler très simple, parfois c'est vraiment utile. Si tout ce que vous avez besoin est de voir si deux tableaux ont les mêmes éléments et ils sont dans le même ordre, essayez ceci:
[1, 2, 3].toString() == [1, 2, 3].toString()
true
[1, 2, 3,].toString() == [1, 2, 3].toString()
true
[1,2,3].toString() == [1, 2, 3].toString()
true
cependant, cela ne fonctionne pas pour les cas avancés de mode tels que:
[[1,2],[3]].toString() == [[1],[2,3]].toString()
true
cela dépend de ce dont vous avez besoin.
jQuery a une telle méthode profonde comparaison récursive .
Une maison à usage général égalité stricte check pourrait ressembler à ce qui suit:
function deepEquals(obj1, obj2, parents1, parents2) {
"use strict";
var i;
// compare null and undefined
if (obj1 === undefined || obj2 === undefined ||
obj1 === null || obj2 === null) {
return obj1 === obj2;
}
// compare primitives
if (typeof (obj1) !== 'object' || typeof (obj2) !== 'object') {
return obj1.valueOf() === obj2.valueOf();
}
// if objects are of different types or lengths they can't be equal
if (obj1.constructor !== obj2.constructor || (obj1.length !== undefined && obj1.length !== obj2.length)) {
return false;
}
// iterate the objects
for (i in obj1) {
// build the parents list for object on the left (obj1)
if (parents1 === undefined) parents1 = [];
if (obj1.constructor === Object) parents1.push(obj1);
// build the parents list for object on the right (obj2)
if (parents2 === undefined) parents2 = [];
if (obj2.constructor === Object) parents2.push(obj2);
// walk through object properties
if (obj1.propertyIsEnumerable(i)) {
if (obj2.propertyIsEnumerable(i)) {
// if object at i was met while going down here
// it's a self reference
if ((obj1[i].constructor === Object && parents1.indexOf(obj1[i]) >= 0) || (obj2[i].constructor === Object && parents2.indexOf(obj2[i]) >= 0)) {
if (obj1[i] !== obj2[i]) {
return false;
}
continue;
}
// it's not a self reference so we are here
if (!deepEquals(obj1[i], obj2[i], parents1, parents2)) {
return false;
}
} else {
// obj2[i] does not exist
return false;
}
}
}
return true;
};
Essais:
// message is displayed on failure
// clean console === all tests passed
function assertTrue(cond, msg) {
if (!cond) {
console.log(msg);
}
}
var a = 'sdf',
b = 'sdf';
assertTrue(deepEquals(b, a), 'Strings are equal.');
b = 'dfs';
assertTrue(!deepEquals(b, a), 'Strings are not equal.');
a = 9;
b = 9;
assertTrue(deepEquals(b, a), 'Numbers are equal.');
b = 3;
assertTrue(!deepEquals(b, a), 'Numbers are not equal.');
a = false;
b = false;
assertTrue(deepEquals(b, a), 'Booleans are equal.');
b = true;
assertTrue(!deepEquals(b, a), 'Booleans are not equal.');
a = null;
assertTrue(!deepEquals(b, a), 'Boolean is not equal to null.');
a = function () {
return true;
};
assertTrue(deepEquals(
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
],
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
]), 'Arrays are equal.');
assertTrue(!deepEquals(
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
],
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': '1'
},
true]
]), 'Arrays are not equal.');
a = {
prop: 'val'
};
a.self = a;
b = {
prop: 'val'
};
b.self = a;
assertTrue(deepEquals(b, a), 'Immediate self referencing objects are equal.');
a.prop = 'shmal';
assertTrue(!deepEquals(b, a), 'Immediate self referencing objects are not equal.');
a = {
prop: 'val',
inside: {}
};
a.inside.self = a;
b = {
prop: 'val',
inside: {}
};
b.inside.self = a;
assertTrue(deepEquals(b, a), 'Deep self referencing objects are equal.');
b.inside.self = b;
assertTrue(!deepEquals(b, a), 'Deep self referencing objects are not equeal. Not the same instance.');
b.inside.self = {foo: 'bar'};
assertTrue(!deepEquals(b, a), 'Deep self referencing objects are not equal. Completely different object.');
a = {};
b = {};
a.self = a;
b.self = {};
assertTrue(!deepEquals(b, a), 'Empty object and self reference of an empty object.');
Vérifier chaque valeur par une boucle une fois que vous avez vérifié la taille du tableau.
function equalArray(a, b) {
if (a.length === b.length) {
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
utilisant map()
et reduce()
:
function arraysEqual (a1, a2) {
return a1 === a2 || (
a1 !== null && a2 !== null &&
a1.length === a2.length &&
a1
.map(function (val, idx) { return val === a2[idx]; })
.reduce(function (prev, cur) { return prev && cur; }, true)
);
}
Il n'y a pas de moyen facile de le faire. J'en avais aussi besoin, mais je voulais une fonction qui puisse prendre deux variables et tester l'égalité. Cela inclut les valeurs non-objet, les objets, les tableaux et n'importe quel niveau d'imbrication.
Dans votre question, vous mentionnez vouloir ignorer l'ordre des valeurs dans un tableau. Ma solution ne le fait pas en soi, mais vous pouvez y parvenir en triant les tableaux avant de comparer pour l'égalité
je voulais aussi le l'option de la coulée de non-objets à cordes de sorte que [1,2]===["1",2]
puisque mon projet utilise des sous-titres, j'ai décidé d'en faire un mixin plutôt qu'une fonction autonome.
vous pouvez le tester sur http://jsfiddle.net/nemesarial/T44W4 /
Voici mon mxin:
_.mixin({
/**
Tests for the equality of two variables
valA: first variable
valB: second variable
stringifyStatics: cast non-objects to string so that "1"===1
**/
equal:function(valA,valB,stringifyStatics){
stringifyStatics=!!stringifyStatics;
//check for same type
if(typeof(valA)!==typeof(valB)){
if((_.isObject(valA) || _.isObject(valB))){
return false;
}
}
//test non-objects for equality
if(!_.isObject(valA)){
if(stringifyStatics){
var valAs=''+valA;
var valBs=''+valB;
ret=(''+valA)===(''+valB);
}else{
ret=valA===valB;
}
return ret;
}
//test for length
if(_.size(valA)!=_.size(valB)){
return false;
}
//test for arrays first
var isArr=_.isArray(valA);
//test whether both are array or both object
if(isArr!==_.isArray(valB)){
return false;
}
var ret=true;
if(isArr){
//do test for arrays
_.each(valA,function(val,idx,lst){
if(!ret){return;}
ret=ret && _.equal(val,valB[idx],stringifyStatics);
});
}else{
//do test for objects
_.each(valA,function(val,idx,lst){
if(!ret){return;}
//test for object member exists
if(!_.has(valB,idx)){
ret=false;
return;
}
// test for member equality
ret=ret && _.equal(val,valB[idx],stringifyStatics);
});
}
return ret;
}
});
Voici comment vous l'utilisez:
_.equal([1,2,3],[1,2,"3"],true)
pour démontrer la nidification, vous pouvez faire ceci:
_.equal(
['a',{b:'b',c:[{'someId':1},2]},[1,2,3]],
['a',{b:'b',c:[{'someId':"1"},2]},["1",'2',3]]
,true);
si vous souhaitez cocher tableaux d'objets pour l'égalité et l'ordre n'a pas d'importance, i.e.
areEqual([{id: "0"}, {id: "1"}], [{id: "1"}, {id: "0"}]) // true
vous devez d'abord trier les tableaux. lodash dispose de tous les outils dont vous avez besoin, en combinant sortBy
et isEqual
:
// arr1 & arr2: Arrays of objects
// sortProperty: the property of the object with which you want to sort
// Note: ensure every object in both arrays has your chosen sortProperty
// For example, arr1 = [{id: "v-test_id0"}, {id: "v-test_id1"}]
// and arr2 = [{id: "v-test_id1"}, {id: "v-test_id0"}]
// sortProperty should be 'id'
function areEqual (arr1, arr2, sortProperty) {
return _.areEqual(_.sortBy(arr1, sortProperty), _.sortBy(arr2, sortProperty))
}
EDIT: puisque sortBy
retourne un nouveau tableau, il n'est pas nécessaire de cloner vos tableaux avant de les trier. Le les matrices originales ne seront pas modifiées.
Notez que pour lodash isEqual
, l'ordre a de l'importance . L'exemple ci-dessus retournera false
si sortBy
n'est pas appliqué à chaque tableau en premier.
Si vous utilisez lodash et ne voulez pas modifier array, vous pouvez utiliser la fonction _.xor () . Il compare les deux tableaux comme des ensembles et retourne l'ensemble qui contient leur différence. Si la longueur de cette différence est zéro, les deux tableaux sont essentiellement égaux:
var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
_.xor(a, b).length === 0
true
_.xor(b, c).length === 0
true
il manipule toutes les choses possibles et même de référence lui-même dans la structure de l'objet. Vous pouvez voir l'exemple à la fin du code.
var deepCompare = (function() {
function internalDeepCompare (obj1, obj2, objects) {
var i, objPair;
if (obj1 === obj2) {
return true;
}
i = objects.length;
while (i--) {
objPair = objects[i];
if ( (objPair.obj1 === obj1 && objPair.obj2 === obj2) ||
(objPair.obj1 === obj2 && objPair.obj2 === obj1) ) {
return true;
}
}
objects.push({obj1: obj1, obj2: obj2});
if (obj1 instanceof Array) {
if (!(obj2 instanceof Array)) {
return false;
}
i = obj1.length;
if (i !== obj2.length) {
return false;
}
while (i--) {
if (!internalDeepCompare(obj1[i], obj2[i], objects)) {
return false;
}
}
}
else {
switch (typeof obj1) {
case "object":
// deal with null
if (!(obj2 && obj1.constructor === obj2.constructor)) {
return false;
}
if (obj1 instanceof RegExp) {
if (!(obj2 instanceof RegExp && obj1.source === obj2.source)) {
return false;
}
}
else if (obj1 instanceof Date) {
if (!(obj2 instanceof Date && obj1.getTime() === obj2.getTime())) {
return false;
}
}
else {
for (i in obj1) {
if (obj1.hasOwnProperty(i)) {
if (!(obj2.hasOwnProperty(i) && internalDeepCompare(obj1[i], obj2[i], objects))) {
return false;
}
}
}
}
break;
case "function":
if (!(typeof obj2 === "function" && obj1+"" === obj2+"")) {
return false;
}
break;
default: //deal with NaN
if (obj1 !== obj2 && obj1 === obj1 && obj2 === obj2) {
return false;
}
}
}
return true;
}
return function (obj1, obj2) {
return internalDeepCompare(obj1, obj2, []);
};
}());
/*
var a = [a, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null],
b = [b, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null];
deepCompare(a, b);
*/
cette méthode craint, mais je l'ai laissée ici pour référence afin que d'autres évitent ce chemin:
en utilisant l'Option 1 de @ninjagecko a fonctionné le mieux pour moi:
Array.prototype.equals = function(array) {
return array instanceof Array && JSON.stringify(this) === JSON.stringify(array) ;
}
a = [1, [2, 3]]
a.equals([[1, 2], 3]) // false
a.equals([1, [2, 3]]) // true
il traitera aussi le cas null et non défini, puisque nous ajoutons cela au prototype de array et vérifions que l'autre argument est aussi un array.
var a= [1, 2, 3, '3'];
var b = [1, 2, 3];
var c = a.filter(function (i) { return ! ~b.indexOf(i); });
alert(c.length);