Comment comparer des tableaux en JavaScript?

991

Je voudrais comparer deux tableaux ... idéalement, efficacement. Rien d'extraordinaire, juste trues'ils sont identiques, etfalse sinon. Sans surprise, l'opérateur de comparaison ne semble pas fonctionner.

var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2);    // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2));    // Returns true

Le codage JSON de chaque tableau le fait, mais existe-t-il un moyen plus rapide ou "meilleur" de comparer simplement les tableaux sans avoir à parcourir chaque valeur?

Julian H. Lam
la source
5
Vous pouvez d'abord comparer leur longueur, et si elles sont égales, chaque valeur.
TJHeuvel
56
Qu'est-ce qui fait que deux tableaux sont égaux pour vous? Mêmes éléments? Même ordre d'éléments? Le codage au format JSON ne fonctionne que tant que l'élément du tableau peut être sérialisé en JSON. Si le tableau peut contenir des objets, à quelle profondeur iriez-vous? Quand deux objets sont-ils "égaux"?
Felix Kling
48
@FelixKling, définir "l'égalité" est certainement un sujet subtil, mais pour les personnes venant de JavaScript à partir de langages de niveau supérieur, il n'y a aucune excuse pour la bêtise comme ([] == []) == false.
Alex D
4
@AlexD, il semble que les tableaux utilisent l'égalité de référence, ce à quoi vous vous attendez. Ce serait assez horrible si vous ne pouviez pas faire ça
JonnyRaa
3
@AlexD Je ne peux pas penser à une langue où cela ne se produit pas. En C ++, vous compareriez deux pointeurs - faux. En Java, vous faites la même chose qu'en javascript. En PHP, quelque chose dans les coulisses passera par les tableaux - appelez-vous PHP un langage de niveau supérieur?
Tomáš Zato - Rétablir Monica

Réponses:

877

Pour comparer des tableaux, parcourez-les et comparez chaque valeur:

Comparaison des tableaux:

// Warn if overriding existing method
if(Array.prototype.equals)
    console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time 
    if (this.length != array.length)
        return false;

    for (var i = 0, l=this.length; i < l; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].equals(array[i]))
                return false;       
        }           
        else if (this[i] != array[i]) { 
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;   
        }           
    }       
    return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

Usage:

[1, 2, [3, 4]].equals([1, 2, [3, 2]]) === false;
[1, "2,3"].equals([1, 2, 3]) === false;
[1, 2, [3, 4]].equals([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].equals([1, 2, 1, 2]) === true;

Vous pouvez dire " Mais il est beaucoup plus rapide de comparer des chaînes - pas de boucles ... " eh bien, alors vous devriez noter qu'il existe des boucles. Première boucle récursive qui convertit Array en chaîne et seconde, qui compare deux chaînes. Cette méthode est donc plus rapide que l'utilisation de chaîne .

Je crois que de plus grandes quantités de données devraient toujours être stockées dans des tableaux, pas dans des objets. Cependant, si vous utilisez des objets, ils peuvent également être partiellement comparés.
Voici comment:

Comparaison d'objets:

J'ai indiqué ci-dessus, que deux instances d' objet ne seront jamais égales, même si elles contiennent les mêmes données pour le moment:

({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666})  //false

Cela a une raison, car il peut y avoir, par exemple, des variables privées dans les objets.

Cependant, si vous utilisez simplement la structure d'objet pour contenir des données, la comparaison est toujours possible:

Object.prototype.equals = function(object2) {
    //For the first loop, we only check for types
    for (propName in this) {
        //Check for inherited methods and properties - like .equals itself
        //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
        //Return false if the return value is different
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        //Check instance type
        else if (typeof this[propName] != typeof object2[propName]) {
            //Different types => not equal
            return false;
        }
    }
    //Now a deeper check using other objects property names
    for(propName in object2) {
        //We must check instances anyway, there may be a property that only exists in object2
            //I wonder, if remembering the checked values from the first loop would be faster or not 
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        else if (typeof this[propName] != typeof object2[propName]) {
            return false;
        }
        //If the property is inherited, do not check any more (it must be equa if both objects inherit it)
        if(!this.hasOwnProperty(propName))
          continue;

        //Now the detail check and recursion

        //This returns the script back to the array comparing
        /**REQUIRES Array.equals**/
        if (this[propName] instanceof Array && object2[propName] instanceof Array) {
                   // recurse into the nested arrays
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        else if (this[propName] instanceof Object && object2[propName] instanceof Object) {
                   // recurse into another objects
                   //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        //Normal value comparison for strings and numbers
        else if(this[propName] != object2[propName]) {
           return false;
        }
    }
    //If everything passed, let's say YES
    return true;
}  

Cependant, n'oubliez pas que celui-ci doit servir à comparer des données similaires à JSON, pas des instances de classe et d'autres choses. Si vous voulez comparer des objets plus complexes, regardez cette réponse et sa fonction super longue .
Pour que cela fonctionne avec, Array.equalsvous devez éditer un peu la fonction d'origine:

...
    // Check if we have nested arrays
    if (this[i] instanceof Array && array[i] instanceof Array) {
        // recurse into the nested arrays
        if (!this[i].equals(array[i]))
            return false;
    }
    /**REQUIRES OBJECT COMPARE**/
    else if (this[i] instanceof Object && array[i] instanceof Object) {
        // recurse into another objects
        //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
        if (!this[i].equals(array[i]))
            return false;
        }
    else if (this[i] != array[i]) {
...

J'ai fait un petit outil de test pour les deux fonctions .

Bonus: tableaux imbriqués avec indexOfetcontains

Samy Bencherif a préparé des fonctions utiles pour le cas où vous recherchez un objet spécifique dans des tableaux imbriqués, qui sont disponibles ici: https://jsfiddle.net/SamyBencherif/8352y6yw/

Tomáš Zato - Réintégrer Monica
la source
27
Si vous voulez faire des comparaisons strictes, utilisez this[i] !== array[i]plutôt que !=.
Tim S.
38
Votre méthode doit être appelée à la equalsplace de compare. Au moins dans .NET, compare renvoie généralement un entier signé indiquant quel objet est plus grand que l'autre. Voir: Comparer.Comparer .
Oliver
15
Ce n'est pas seulement la bonne façon de procéder, c'est aussi beaucoup plus efficace. Voici un script jsperf rapide que j'ai préparé pour toutes les méthodes suggérées dans cette question. jsperf.com/comparing-arrays2
Tolga E
96
Changer le prototype d'un type intégré n'est certainement pas la bonne façon
Jasper
31
De plus, il ne s'agit pas de savoir s'il est facile de réécrire, c'est du fait qu'une réponse ne devrait pas recommander quelque chose qui est considéré comme une mauvaise pratique ( developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/… ) et devrait certainement pas faire cela sous l'en-tête "La bonne façon"
Jasper
386

Bien que cela ne fonctionne que pour les tableaux scalaires (voir la note ci-dessous), il est court:

array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})

Rr, dans ECMAScript 6 / CoffeeScript / TypeScript avec fonctions fléchées:

array1.length === array2.length && array1.every((value, index) => value === array2[index])

(Remarque: «scalaire» signifie ici des valeurs qui peuvent être comparées directement en utilisant ===. Donc: nombres, chaînes, objets par référence, fonctions par référence. Voir la référence MDN pour plus d'informations sur les opérateurs de comparaison).

MISE À JOUR

D'après ce que j'ai lu dans les commentaires, le tri du tableau et la comparaison peuvent donner un résultat précis:

array1.length === array2.length && array1.sort().every(function(value, index) { return value === array2.sort()[index]});

Par exemple:

array1 = [2,3,1,4];
array2 = [1,2,3,4];

Ensuite, le code ci-dessus donnerait true

user2782196
la source
19
J'aime cela, bien que les lecteurs doivent savoir que cela ne fonctionne que sur des tableaux triés.
Ellen Spertus,
13
Il fonctionne sur tout type de tableaux, triés ou non @espertus
Michał Miszczyszyn
36
Oui, exactement. Cette fonction est censée comparer deux tableaux, peu importe qu'ils soient triés ou non, leurs éléments consécutifs doivent être égaux.
Michał Miszczyszyn
22
@espertus En effet, cela ne retournera pas vrai si les éléments n'ont pas exactement le même ordre dans les deux tableaux. Cependant, le but d'une vérification d'égalité n'est pas de vérifier s'ils contiennent les mêmes éléments mais de vérifier s'ils ont le même élément dans les mêmes ordres.
Quentin Roy
7
Si vous voulez vérifier si les deux tableaux sont égaux, contenant les mêmes éléments non triés (mais pas utilisés plusieurs fois), vous pouvez utiliser a1.length==a2.length && a1.every((v,i)=>a2.includes(v)): var a1 =[1,2,3], a2 = [3,2,1];( var a1 =[1,3,3], a2 = [1,1,3];ne fonctionnera pas comme prévu)
mems
208

J'aime utiliser la bibliothèque Underscore pour les projets de codage lourds de tableaux / objets ... dans Underscore et Lodash, que vous compariez des tableaux ou des objets, cela ressemble à ceci:

_.isEqual(array1, array2)   // returns a boolean
_.isEqual(object1, object2) // returns a boolean
Jason Boerner
la source
22
Notez que l'ordre est important _.isEqual([1,2,3], [2,1,3]) => false
Vitaliy Alekask
3
ou si vous voulez juste la isEqualfonctionnalité, vous pouvez toujours utiliser le module
lodash.isequal
6
Vous pouvez peut-être utiliser _.difference (); si l'ordre n'a pas d'importance pour vous
Ronan Quillevere
5
Nous pouvons trier le tableau avant cette vérification si la commande _.isEqual([1,2,3].sort(), [2,1,3].sort()) => true
n'a
réponse la plus concise et la plus simple à mon humble avis :-)
Kieran Ryan
121

Je pense que c'est la façon la plus simple de le faire en utilisant JSON stringify, et cela peut être la meilleure solution dans certaines situations:

JSON.stringify(a1) === JSON.stringify(a2);

Cela convertit les objets a1eta2 en chaînes afin qu'ils puissent être comparés. L'ordre est important dans la plupart des cas, car cela peut trier l'objet à l'aide d'un algorithme de tri indiqué dans l'une des réponses ci-dessus.

Veuillez noter que vous ne comparez plus l'objet mais la représentation sous forme de chaîne de l'objet. Ce n'est peut-être pas exactement ce que vous voulez.

radtek
la source
bonne réponse mais pourquoi [] == [] retourne faux? les deux sont des objets simples alors pourquoi?
Pardeep Jain
4
@PardeepJain, cela est dû au fait que, par défaut, l'opérateur d'égalité dans ECMAScript for Objects renvoie true lorsqu'ils font référence au même emplacement de mémoire. Essayez var x = y = []; // maintenant l'égalité renvoie vrai.
radtek
7
juste pour noter que la fonction JSON stringify n'est pas rapide. Utilisé avec des tableaux plus grands introduira certainement un retard.
Lukas Liesis
6
La question demande spécifiquement s'il existe un moyen meilleur / plus rapide que d'utiliser JSON.stringify.
Don Hatch
Il explique plus en détail pourquoi cela peut être une bonne solution dans certaines situations.
radtek
61

On ne sait pas ce que vous entendez par «identique». Par exemple, les tableaux aet bci - dessous sont-ils identiques (notez les tableaux imbriqués)?

var a = ["foo", ["bar"]], b = ["foo", ["bar"]];

Voici une fonction de comparaison de tableau optimisée qui compare tour à tour les éléments correspondants de chaque tableau en utilisant une stricte égalité et ne fait pas de comparaison récursive des éléments de tableau qui sont eux-mêmes des tableaux, ce qui signifie que pour l'exemple ci-dessus, arraysIdentical(a, b)cela reviendrait false. Cela fonctionne dans le cas général, que les join()solutions basées sur JSON et ne permettront pas:

function arraysIdentical(a, b) {
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
        if (a[i] !== b[i]) return false;
    }
    return true;
};
Tim Down
la source
@ASDF: La question de savoir ce que "identique" signifie n'est pas claire. De toute évidence, cette réponse ne fait qu'une vérification superficielle. Je vais ajouter une note.
Tim Down du
cela échoue pour arraysIdentical ([1, 2, [3, 2]], [1, 2, [3, 2]]);
Gopinath Shiva
4
@GopinathShiva: Eh bien, cela échoue uniquement si vous vous attendez à ce qu'il revienne true. La réponse explique que non. Si vous devez comparer des tableaux imbriqués, vous pouvez facilement ajouter une vérification récursive.
Tim Down
59

La voie pratique

Je pense qu'il est faux de dire qu'une implémentation particulière est "The Right Way ™" si elle n'est que "correcte" ("correcte") contrairement à une "mauvaise" solution. La solution de Tomáš est une nette amélioration par rapport à la comparaison de tableaux basés sur des chaînes, mais cela ne signifie pas qu'elle est objectivement "correcte". Qu'est-ce qui est juste toute façon? Est-ce le plus rapide? Est-ce le plus flexible? Est-ce le plus simple à comprendre? Est-ce le débogage le plus rapide? Utilise-t-il le moins d'opérations? Est-ce que cela a des effets secondaires? Aucune solution ne peut avoir le meilleur de toutes choses.

Tomáš pourrait dire que sa solution est rapide mais je dirais aussi que c'est inutilement compliqué. Il essaie d'être une solution tout-en-un qui fonctionne pour toutes les baies, imbriquées ou non. En fait, il accepte même plus que de simples tableaux en entrée et tente toujours de donner une réponse "valide".


Les génériques offrent une réutilisabilité

Ma réponse abordera le problème différemment. Je vais commencer par une arrayCompareprocédure générique qui ne concerne que la navigation dans les tableaux. À partir de là, nous allons construire nos autres fonctions de comparaison de base comme arrayEqualet arrayDeepEqual, etc.

// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
  x === undefined && y === undefined
    ? true
    : Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)

À mon avis, le meilleur type de code n'a même pas besoin de commentaires, et cela ne fait pas exception. Il se passe tellement peu de choses ici que vous pouvez comprendre le comportement de cette procédure sans presque aucun effort. Bien sûr, une partie de la syntaxe ES6 peut vous sembler étrangère maintenant, mais c'est uniquement parce que ES6 est relativement nouveau.

Comme le type le suggère, arrayCompareprend la fonction de comparaison f, et deux tableaux d'entrée, xset ys. Pour la plupart, tout ce que nous faisons est d'appeler f (x) (y)pour chaque élément dans les tableaux d'entrée. Nous retournons tôt falsesi la valeur définie par l'utilisateur frevient false- grâce à &&l'évaluation des courts-circuits de. Donc oui, cela signifie que le comparateur peut arrêter l'itération plus tôt et empêcher le bouclage à travers le reste du tableau d'entrée lorsqu'il n'est pas nécessaire.


Comparaison stricte

Ensuite, en utilisant notre arrayComparefonction, nous pouvons facilement créer d'autres fonctions dont nous pourrions avoir besoin. Commençons par l'élémentaire arrayEqual

// equal :: a -> a -> Bool
const equal = x => y =>
  x === y // notice: triple equal

// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
  arrayCompare (equal)

const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys))      //=> true
// (1 === 1) && (2 === 2) && (3 === 3)  //=> true

const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs))      //=> false
// (1 === '1')                          //=> false

Aussi simple que cela. arrayEqualpeut être défini avec arrayCompareet une fonction de comparaison qui se compare aà l' butilisation=== (pour une stricte égalité).

Notez que nous définissons également equalcomme sa propre fonction. Cela met en évidence le rôle d' arrayCompareune fonction d'ordre supérieur pour utiliser notre comparateur de premier ordre dans le contexte d'un autre type de données (Array).


Comparaison lâche

Nous pourrions tout aussi facilement définir en arrayLooseEqualutilisant un à la ==place. Maintenant, en comparant 1(Number) à '1'(String), le résultat sera true

// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
  x == y // notice: double equal

// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
  arrayCompare (looseEqual)

const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys))    //=> true
// (1 == '1') && (2 == '2') && (3 == '3')  //=> true

Comparaison approfondie (récursive)

Vous avez probablement remarqué que ce n'est qu'une comparaison superficielle. La solution de Tomáš est sûrement "The Right Way ™" car elle implique une comparaison profonde implicite, n'est-ce pas?

Eh bien, notre arrayCompareprocédure est suffisamment polyvalente pour être utilisée de manière à ce qu'un test d'égalité approfondi soit un jeu d'enfant…

// isArray :: a -> Bool
const isArray =
  Array.isArray

// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
  arrayCompare (a => b =>
    isArray (a) && isArray (b)
      ? arrayDeepCompare (f) (a) (b)
      : f (a) (b))

const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3')         //=> false

console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3')                 //=> true

Aussi simple que cela. Nous construisons un comparateur profond en utilisant une autre fonction d'ordre supérieur. Cette fois, nous arrayCompareutilisons un comparateur personnalisé qui vérifiera si aet bsont des tableaux. Si tel est le cas, réappliquez arrayDeepComparesinon comparer aet bau comparateur spécifié par l'utilisateur ( f). Cela nous permet de séparer le comportement de comparaison approfondie de la façon dont nous comparons réellement les éléments individuels. C'est-à-dire, comme le montre l'exemple ci-dessus, nous pouvons comparer en profondeur en utilisant equal,looseEqual ou tout autre comparateur que nous faisons.

Parce qu'il arrayDeepCompareest curry, nous pouvons l'appliquer partiellement comme nous l'avons fait dans les exemples précédents

// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
  arrayDeepCompare (equal)

// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
  arrayDeepCompare (looseEqual)

Pour moi, c'est déjà une nette amélioration par rapport à la solution de Tomáš car je peux explicitement choisir une comparaison superficielle ou profonde pour mes tableaux, selon les besoins.


Comparaison d'objets (exemple)

Et si vous avez un tableau d'objets ou quelque chose? Peut-être que vous voulez considérer ces tableaux comme «égaux» si chaque objet a la même idvaleur…

// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
  x.id !== undefined && x.id === y.id

// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
  arrayCompare (idEqual)

const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2)            //=> true

const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6)            //=> false

Aussi simple que cela. Ici, j'ai utilisé des objets JS vanilla, mais ce type de comparateur pourrait fonctionner pour n'importe quel type d'objet; même vos objets personnalisés. La solution de Tomáš devrait être complètement retravaillée pour prendre en charge ce type de test d'égalité

Tableau profond avec des objets? Pas de problème. Nous avons créé des fonctions génériques très polyvalentes, afin qu'elles fonctionnent dans une grande variété de cas d'utilisation.

const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys))     //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true

Comparaison arbitraire (exemple)

Ou si vous vouliez faire une autre sorte de comparaison complètement arbitraire? Peut-être que je veux savoir si chacun xest plus grand que chacun y

// gt :: Number -> Number -> Bool
const gt = x => y =>
  x > y

// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)

const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys))     //=> true
// (5 > 2) && (10 > 4) && (20 > 8)  //=> true

const zs = [6,12,24]
console.log (arrayGt (xs) (zs))     //=> false
// (5 > 6)                          //=> false

Moins est plus

Vous pouvez voir que nous faisons plus avec moins de code. Il n'y a rien de compliqué en arrayComparesoi et chacun des comparateurs personnalisés que nous avons créés a une implémentation très simple.

Avec facilité, nous pouvons définir exactement comment nous voulons deux tableaux à comparer - peu profond, profond, stricte, lâche, une propriété d'objet, ou un calcul arbitraire, ou toute combinaison de ceux - ci - tout en utilisant une procédure , arrayCompare. Peut-être même imaginer un RegExpcomparateur! Je sais à quel point les enfants adorent ces expressions régulières…

Est-ce le plus rapide? Nan. Mais ce n'est probablement pas nécessaire non plus. Si la vitesse est la seule métrique utilisée pour mesurer la qualité de notre code, beaucoup de très bon code serait jeté - C'est pourquoi j'appelle cette approche The Practical Way . Ou peut-être pour être plus juste, A pratique. Cette description convient à cette réponse parce que je ne dis pas que cette réponse n'est pratique que par rapport à une autre réponse; c'est objectivement vrai. Nous avons atteint un degré élevé de fonctionnalité avec très peu de code très facile à raisonner. Aucun autre code ne peut dire que nous n'avons pas mérité cette description.

Est-ce que cela en fait la «bonne» solution pour vous? C'est à vous de décider. Et personne d'autre ne peut le faire pour vous; vous seul savez quels sont vos besoins. Dans presque tous les cas, j'apprécie le code simple, pratique et polyvalent plutôt que le type intelligent et rapide. Ce que vous appréciez peut différer, alors choisissez ce qui vous convient.


Éditer

Mon ancienne réponse était plus axée sur la décomposition arrayEqualen minuscules procédures. C'est un exercice intéressant, mais ce n'est pas vraiment la meilleure façon (la plus pratique) d'aborder ce problème. Si vous êtes intéressé, vous pouvez voir cet historique de révision.

Je vous remercie
la source
8
"le meilleur type de code n'a même pas besoin de commentaires" ... déteste le dire, mais ce code pourrait utiliser plus d'un commentaire, et / ou un nom différent - "comparer" est assez vague. Si je lis correctement, votre "comparaison" est essentiellement un "tous" récursif au curry. Je pense. Ou est-ce un "certains" récursif au curry? Hmm. Cela nécessite plus de réflexion que nécessaire. Un meilleur nom serait peut-être «tableaux équivalents», en utilisant la terminologie standard de «relation d'équivalence». Ou, encore plus clair (pour moi en tout cas), "récursivement équivalent".
Don Hatch
1
@DonHatch merci pour l'opportunité de répondre. Par «comparer», voulez-vous dire arrayCompare? Oui, la fonction est curry, mais elle diffère de someet every. arrayCompareprend un comparateur et deux tableaux pour comparer. J'ai choisi un nom spécifiquement générique car nous pouvons comparer des tableaux en utilisant n'importe quelle fonction arbitraire. La fonction est curry afin qu'elle puisse être spécialisée pour créer de nouvelles fonctions de comparaison de tableaux (par exemple,arrayEqual ). Pouvez-vous suggérer un meilleur nom? Dans quels domaines pensez-vous avoir besoin de commentaires ou d'explications supplémentaires? Je suis heureux de discuter ^ _ ^
Merci
1
Je ne sais pas encore si mon point est clair - mais mon point est que votre fonction n'est pas vraiment destinée à prendre une fonction arbitraire , je ne pense pas - elle est destinée à prendre une relation d'équivalence , et elle renvoie une relation d'équivalence. C'est important - cela ne ferait rien de sensé (je ne pense pas) si on donnait un autre type de fonction binaire arbitraire comme celles que j'ai mentionnées, même celles que les gens appellent souvent "comparer". Je pense donc qu'il serait utile de mettre "équivalent" au nom de "comparer".
Don Hatch
1
@ftor, auteur: réponse super utile, beau travail, +1. Commentaires: vous préconisez la simplicité, mais aucune expression avec trois flèches sur une seule ligne n'est simple ou facile à comprendre pour de nombreux développeurs. Par exemple: f => ([x, ... xs]) => ([y, ... ys]) =>. Je l'utilise constamment et je devais encore le décomposer mentalement, plutôt que de "simplement le regarder". Le deuxième point est correct, utilisez tout. Même en pesant vos raisons, dans l'ensemble, cela me semble mieux non seulement, mais aussi de votre point de vue lorsque vous essayez de déduire votre philosophie de conception.
whitneyland
1
Je comprends que c'est un lieu d'apprentissage, mais je fais ici l'hypothèse que le programmeur moyen étudiant le style fonctionnel peut transformer n'importe quelle fonction curry en fonction non curry. Ma réponse ne suggère pas que ce style est destiné à être utilisé dans votre propre programme - écrivez-le non, écrivez-le en utilisant vos propres règles d'indentation, écrivez-le comme vous le souhaitez - j'écris mes réponses dans un style qui, je crois, exprime le programme le mieux. J'aime aussi inviter les autres à contester la façon dont nous exprimons nos programmes de manière syntaxique
Merci
54

Dans l'esprit de la question d'origine:

Je voudrais comparer deux tableaux ... idéalement, efficacement . Rien d'extraordinaire , juste vrai s'ils sont identiques, et faux sinon.

J'ai effectué des tests de performances sur certaines des suggestions les plus simples proposées ici avec les résultats suivants (rapide à lent):

tandis que (67%) par Tim Down

var i = a1.length;
while (i--) {
    if (a1[i] !== a2[i]) return false;
}
return true

chaque (69%) par utilisateur2782196

a1.every((v,i)=> v === a2[i]);

réduire (74%) par les DEI

a1.reduce((a, b) => a && a2.includes(b), true);

join & toString (78%) par Gaizka Allende & vivek

a1.join('') === a2.join('');

a1.toString() === a2.toString();

demi toString (90%) par Victor Palomo

a1 == a2.toString();

stringify (100%) par radtek

JSON.stringify(a1) === JSON.stringify(a2);

Remarque les exemples ci-dessous supposent que les tableaux sont des tableaux triés unidimensionnels. .lengthla comparaison a été supprimée pour une référence commune (ajoutez a1.length === a2.lengthà l'une des suggestions et vous obtiendrez une amélioration des performances de ~ 10%). Choisissez les solutions qui vous conviennent le mieux, en connaissant la vitesse et les limites de chacune.

Remarque indépendante: il est intéressant de voir que les gens obtiennent tous John Waynes heureux du déclencheur sur le bouton de vote pour des réponses parfaitement légitimes à cette question.

unitario
la source
Le lien ouvre un test vide.
Alexander Abakumov
Si vous augmentez la taille du tableau, ces chiffres ne s'appliquent pas (en particulier l'approche de réduction). Essayez avec Array.from({length: 1000}).map((a,v)=> $ {v}.padStart(10,2));
Narayon
cela ne fonctionne que pour un tableau peu profond
Ramesh Rajendran
28

En s'appuyant sur la réponse de Tomáš Zato, je conviens que le simple fait de parcourir les tableaux est le plus rapide. De plus (comme d'autres l'ont déjà dit), la fonction doit être appelée égal / égal, pas comparer. À la lumière de cela, j'ai modifié la fonction pour gérer la comparaison des tableaux pour la similitude - c'est-à-dire qu'ils ont les mêmes éléments, mais hors service - pour un usage personnel, et j'ai pensé que je la mettrais ici pour que tout le monde puisse la voir.

Array.prototype.equals = function (array, strict) {
    if (!array)
        return false;

    if (arguments.length == 1)
        strict = true;

    if (this.length != array.length)
        return false;

    for (var i = 0; i < this.length; i++) {
        if (this[i] instanceof Array && array[i] instanceof Array) {
            if (!this[i].equals(array[i], strict))
                return false;
        }
        else if (strict && this[i] != array[i]) {
            return false;
        }
        else if (!strict) {
            return this.sort().equals(array.sort(), true);
        }
    }
    return true;
}

Cette fonction prend un paramètre supplémentaire strict qui vaut par défaut true. Ce paramètre strict définit si les tableaux doivent être totalement égaux dans le contenu et l'ordre de ces contenus, ou simplement contenir le même contenu.

Exemple:

var arr1 = [1, 2, 3, 4];
var arr2 = [2, 1, 4, 3];  // Loosely equal to 1
var arr3 = [2, 2, 3, 4];  // Not equal to 1
var arr4 = [1, 2, 3, 4];  // Strictly equal to 1

arr1.equals(arr2);         // false
arr1.equals(arr2, false);  // true
arr1.equals(arr3);         // false
arr1.equals(arr3, false);  // false
arr1.equals(arr4);         // true
arr1.equals(arr4, false);  // true

J'ai également rédigé un rapide jsfiddle avec la fonction et cet exemple:
http://jsfiddle.net/Roundaround/DLkxX/

Evan Steinkerchner
la source
12

Même si cela a beaucoup de réponses, une que je pense être utile:

const newArray = [ ...new Set( [...arr1, ...arr2] ) ]

Il n'est pas indiqué dans la question à quoi ressemblera la structure du tableau, donc si vous savez avec certitude que vous n'aurez pas de tableaux ni d'objets imbriqués dans votre tableau (cela m'est arrivé, c'est pourquoi je suis arrivé à ce réponse) le code ci-dessus fonctionnera.

Ce qui se passe, c'est que nous utilisons l'opérateur d'étalement (...) pour concaténer les deux tableaux, puis nous utilisons Set pour éliminer les doublons. Une fois que vous avez cela, vous pouvez comparer leurs tailles, si les trois tableaux ont la même taille, vous êtes prêt à partir.

Cette réponse ignore également l'ordre des éléments , comme je l'ai dit, la situation exacte m'est arrivée, donc peut-être que quelqu'un dans la même situation pourrait se retrouver ici (comme moi).


Modifier 1.

Répondre à la question de Dmitry Grinko: "Pourquoi avez-vous utilisé l'opérateur de propagation (...) ici - ... nouveau Set? Cela ne fonctionne pas"

Considérez ce code:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ new Set( [...arr1, ...arr2] ) ]
console.log(newArray)

Tu auras

[ Set { 'a', 'b', 'c' } ]

Pour travailler avec cette valeur, vous devez utiliser certaines propriétés Set (voir https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set ). En revanche, lorsque vous utilisez ce code:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
console.log(newArray)

Tu auras

[ 'a', 'b', 'c' ]

C'est la différence, le premier me donnerait un ensemble, cela fonctionnerait aussi car je pourrais obtenir la taille de cet ensemble, mais le second me donne le tableau dont j'ai besoin, ce qui est plus direct avec la résolution.

Jeferson Euclides
la source
Pourquoi avez-vous utilisé l'opérateur spread (...) ici - ... nouveau Set? Ça ne marche pas.
Dmitry Grinko
Dmitry Grinko Je crois avoir répondu à votre question sur mon Edit1. Mais je ne suis pas sûr de ce que vous vouliez dire en disant «cela ne fonctionne pas», car les deux réponses peuvent vous
gêner
10

Sur les mêmes lignes que JSON.encode est d'utiliser join ().

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;


    //slice so we do not effect the original
    //sort makes sure they are in order
    //join makes it a string so we can do a string compare
    var cA = arrA.slice().sort().join(","); 
    var cB = arrB.slice().sort().join(",");

    return cA===cB;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];  //will return true

console.log( checkArrays(a,b) );  //true
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //true

Le seul problème est que si vous vous souciez des types testés par la dernière comparaison. Si vous vous souciez des types, vous devrez boucler.

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;

    //slice so we do not effect the orginal
    //sort makes sure they are in order
    var cA = arrA.slice().sort(); 
    var cB = arrB.slice().sort();

    for(var i=0;i<cA.length;i++){
         if(cA[i]!==cB[i]) return false;
    }

    return true;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];

console.log( checkArrays(a,b) );  //true
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //false

Si l'ordre doit rester le même, c'est juste une boucle, aucun tri n'est nécessaire.

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;


    for(var i=0;i<arrA.length;i++){
         if(arrA[i]!==arrB[i]) return false;
    }

    return true;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];

console.log( checkArrays(a,a) );  //true
console.log( checkArrays(a,b) );  //false
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //false
epascarello
la source
3
Cela ne fonctionne que pour certains tableaux et sera très lent avec les grands tableaux.
Tomáš Zato - Rétablir Monica
2
La génération de JSON est également en boucle, vous ne le savez pas (ou il semble que oui). Outre le bouclage, la génération de JSON nécessite également plus de mémoire - elle crée 2 représentations de chaîne desdits tableaux avant de comparer. La fonction downwote est implémentée pour classer les réponses du meilleur au pire. Je pense que votre réponse n'est pas une bonne réponse, alors je l'ai rejetée.
Tomáš Zato - Rétablir Monica
2
Désolé, je viens de dire JSON au lieu de .join(). Peut-être que si vous déclariez votre deuxième solution comme principale (car c'est la meilleure, bien qu'inutile contre les tableaux multidimensionnels), je ne vous jugerais pas de cette façon. Jusqu'à présent, j'ai rétrogradé toutes les réponses qui convertissent les tableaux en chaînes. De plus, j'ai voté pour tous ceux qui utilisent la bonne façon, au cas où vous en auriez besoin. Cela signifie la réponse de @Tim Down et celle de Bireys.
Tomáš Zato - Rétablir Monica
6
Échec de la première version:, checkArrays([1,2,3] , ["1,2",3]) == trueet il est très peu probable que ce soit ce que vous voulez faire!
Doin
2
@epascarello: Oui, vous pouvez mais (à part l'inefficacité du très long séparateur que vous suggérez), cela signifie qu'il y aura des cas extrêmes (où le tableau contiendra une chaîne contenant votre séparateur) où la fonction checkArrays () se comporte mal . Cela pourrait ne pas être un problème si vous savez quelque chose sur le contenu des tableaux (vous pouvez donc choisir un séparateur dont vous êtes sûr qu'il ne sera pas dans les éléments du tableau), mais si vous essayez d'écrire une comparaison générale de tableaux fonction, puis en utilisant join()comme ça le rend subtilement buggé!
Doin
7

Voici une version Typescript:

//https://stackoverflow.com/a/16436975/2589276
export function arraysEqual<T>(a: Array<T>, b: Array<T>): boolean {
    if (a === b) return true
    if (a == null || b == null) return false
    if (a.length != b.length) return false

    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false
    }
    return true
}

//https://stackoverflow.com/a/16436975/2589276
export function arraysDeepEqual<T>(a: Array<T>, b: Array<T>): boolean {
    return JSON.stringify(a) === JSON.stringify(b)
}

Quelques cas de test pour le moka:

it('arraysEqual', function () {
    let a = [1,2]
    let b = [1,2]
    let c = [2,3]
    let d = [2, 3]
    let e = ['car','apple','banana']
    let f = ['car','apple','banana']
    let g = ['car','apple','banan8']

    expect(arraysEqual(a, b)).to.equal(true)
    expect(arraysEqual(c, d)).to.equal(true)
    expect(arraysEqual(a, d)).to.equal(false)
    expect(arraysEqual(e, f)).to.equal(true)
    expect(arraysEqual(f, g)).to.equal(false)
})

it('arraysDeepEqual', function () {
    let a = [1,2]
    let b = [1,2]
    let c = [2,3]
    let d = [2, 3]
    let e = ['car','apple','banana']
    let f = ['car','apple','banana']
    let g = ['car','apple','banan8']
    let h = [[1,2],'apple','banan8']
    let i = [[1,2],'apple','banan8']
    let j = [[1,3],'apple','banan8']

    expect(arraysDeepEqual(a, b)).to.equal(true)
    expect(arraysDeepEqual(c, d)).to.equal(true)
    expect(arraysDeepEqual(a, d)).to.equal(false)
    expect(arraysDeepEqual(e, f)).to.equal(true)
    expect(arraysDeepEqual(f, g)).to.equal(false)
    expect(arraysDeepEqual(h, i)).to.equal(true)
    expect(arraysDeepEqual(h, j)).to.equal(false)
})
Esqarrouth
la source
6

Si vous utilisez un framework de test comme Mocha avec la bibliothèque d'assertions Chai , vous pouvez utiliser l' égalité profonde pour comparer les tableaux.

expect(a1).to.deep.equal(a2)

Cela ne devrait retourner vrai que si les tableaux ont des éléments égaux aux indices correspondants.

metakermit
la source
6

S'il ne s'agit que de deux tableaux de nombres ou de chaînes, il s'agit d'une ligne rapide

const array1 = [1, 2, 3];
const array2 = [1, 3, 4];
console.log(array1.join(',') === array2.join(',')) //false

const array3 = [1, 2, 3];
const array4 = [1, 2, 3];
console.log(array3.join(',') === array4.join(',')) //true
Gaizka Allende
la source
const array1 = [1]; const array2 = [1, 1]; console.log (array1.join ('') === array2.join ('')) // renvoie vrai
Dan M.
il ne devrait pas: array1.join ('') est '1' et array2.join ('') est '11'
Gaizka Allende
désolé, faute de frappe. Le premier tableau devrait être [11]. Assez évident pour expliquer pourquoi cela se produit et comment y remédier.
Dan M.
Je ne sais pas de quoi vous parlez, c'est assez simple: [1] .join () est "1" et [1,1] .join () est "1,1", donc ils ne seront jamais égaux
Gaizka Allende
s'il vous plaît, relisez mon commentaire plus attentivement. Si vous ne le voyez toujours pas, veuillez prendre un butin à ideone.com/KFu427
Dan M.
5

Dans mon cas, les tableaux comparés ne contiennent que des nombres et des chaînes. Cette fonction vous montrera si les tableaux contiennent les mêmes éléments.

function are_arrs_match(arr1, arr2){
    return arr1.sort().toString() === arr2.sort().toString()
}

Testons-le!

arr1 = [1, 2, 3, 'nik']
arr2 = ['nik', 3, 1, 2]
arr3 = [1, 2, 5]

console.log (are_arrs_match(arr1, arr2)) //true
console.log (are_arrs_match(arr1, arr3)) //false
yesnik
la source
La question ne vous demande pas de trier, donc votre solution est fausse pour des exemples comme are_arrs_equal([1,2], [2,1]). Consultez également d'autres discussions sur cette page pour savoir pourquoi la stringence est inutile, fragile et erronée.
traitez bien vos mods
are_arrs_equal([1,2], [2,1])renvoie truecomme prévu. Cette solution n'est peut-être pas idéale, mais elle a fonctionné pour moi.
yesnik
C'est précisément le problème, ces deux ne sont pas égaux dans un sens sensé du mot «égal» pour une structure de données ordonnée . Ce sont des tableaux, pas des ensembles, et si vous voulez l'égalité des ensembles, vous devez l'appeler ainsi - et répondre à une question différente. :-)
traitez bien vos mods
1
Je suis d'accord avec les commentaires ci-dessus, mais cette solution fonctionne également pour moi dans mes tableaux simples d'entiers, où l'ordre n'est pas important, donc je vais l'utiliser.
tomazahlin
1
Échoue pour are_arrs_match([1,2], ["1,2"])(retourne true). Et notez que l' the sort()appel modifiera les tableaux d'entrée - cela pourrait ne pas être souhaitable.
try-catch-finally
5

Cela compare 2 tableaux non triés:

function areEqual(a, b) {
  if ( a.length != b.length) {
    return false;
  }
  return a.filter(function(i) {
    return !b.includes(i);
  }).length === 0;  
}
Nathan Boolean Trujillo
la source
Bien que coûteuse (en termes de ressources de calcul), il s'agit d'une solution robuste qui devrait convenir à différents types et ne repose pas sur le tri!
user3.1415927
5

Pour un tableau de nombres, essayez:

a1==''+a2

Remarque: cette méthode ne fonctionnera pas lorsque le tableau contient également des chaînes, par exemple a2 = [1, "2,3"].

Kamil Kiełczewski
la source
astuce intelligente ..
javadba
4

Nous pourrions le faire de manière fonctionnelle, en utilisant every( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/every )

function compareArrays(array1, array2) {
    if (array1.length === array2.length)
        return array1.every((a, index) => a === array2[index])
    else
        return false
}

// test
var a1 = [1,2,3];
var a2 = [1,2,3];

var a3 = ['a', 'r', 'r', 'a', 'y', '1']
var a4 = ['a', 'r', 'r', 'a', 'y', '2']

console.log(compareArrays(a1,a2)) // true
console.log(compareArrays(a1,a3)) // false
console.log(compareArrays(a3,a4)) // false
peonicles
la source
4

Votre code ne traitera pas le cas de manière appropriée lorsque les deux tableaux ont les mêmes éléments mais pas dans le même ordre.

Jetez un oeil à mon code avec votre exemple qui compare deux tableaux dont les éléments sont des nombres, vous pouvez le modifier ou l'étendre pour d'autres types d'éléments (en utilisant .join () au lieu de .toString ()).

var a1 = [1,2,3];
var a2 = [1,2,3];
const arraysAreEqual = a1.sort().toString()==a2.sort().toString();
// true if both arrays have same elements else false
console.log(arraysAreEqual);

durga patra
la source
3

Voici ma solution:

/**
 * Tests two data structures for equality
 * @param {object} x
 * @param {object} y
 * @returns {boolean}
 */
var equal = function(x, y) {
    if (typeof x !== typeof y) return false;
    if (x instanceof Array && y instanceof Array && x.length !== y.length) return false;
    if (typeof x === 'object') {
        for (var p in x) if (x.hasOwnProperty(p)) {
            if (typeof x[p] === 'function' && typeof y[p] === 'function') continue;
            if (x[p] instanceof Array && y[p] instanceof Array && x[p].length !== y[p].length) return false;
            if (typeof x[p] !== typeof y[p]) return false;
            if (typeof x[p] === 'object' && typeof y[p] === 'object') { if (!equal(x[p], y[p])) return false; } else
            if (x[p] !== y[p]) return false;
        }
    } else return x === y;
    return true;
};

Fonctionne avec n'importe quelle structure de données imbriquée et ignore évidemment les méthodes des objets. Ne pensez même pas à étendre Object.prototype avec cette méthode, quand j'ai essayé une fois, jQuery s'est cassé;)

Pour la plupart des baies, c'est toujours plus rapide que la plupart des solutions de sérialisation. C'est probablement la méthode de comparaison la plus rapide pour les tableaux d'enregistrements d'objets.

Harry
la source
pas bien! ceux-ci donnent vrai: equal({}, {a:1})et equal({}, null)et cela se trompe:equal({a:2}, null)
kristianlm
3
JSON.stringify(collectionNames).includes(JSON.stringify(sourceNames)) ?  array.push(collection[i]) : null

C'est comme ça que je l'ai fait.

Leed
la source
Bonne solution - mais je me demande dans certaines situations si cela ne fonctionnera pas toujours comme prévu, comme avec certaines primitives ou tableaux profondément imbriqués? J'espère que cela fonctionne dans toutes les circonstances
Ben Rondeau
3

Comparaison de 2 tableaux:

var arr1 = [1,2,3];
var arr2 = [1,2,3];

function compare(arr1,arr2)
{
  if((arr1 == arr2) && (arr1.length == arr2.length))
    return true;
  else
    return false;
}

fonction d'appel

var isBool = compare(arr1.sort().join(),arr2.sort().join());
Amay Kulkarni
la source
Cette réponse ne fonctionnera pas, car le === ne se comporte pas comme prévu pour les tableaux.
Michael Yang
La réponse fonctionne, bien que === n'ait aucune signification ici (puisque sort () ne fonctionne que sur le tableau). Même == fonctionnera également.
Amay Kulkarni
Essayez-le vous-même; il affiche false si vous exécutez ce code. Cela est dû à la comparaison des valeurs de référence des tableaux par == et === au lieu de leurs valeurs réelles. Les == et === sont uniquement destinés à la comparaison de types primitifs.
Michael Yang
Il retourne vrai, nous l'avons utilisé, mais j'ai supprimé '===' maintenant car ce n'est pas nécessaire
Amay Kulkarni
Ah, je n'ai pas remarqué que vous convertissiez en chaîne et appeliez la fonction après le tri et la jointure; mes excuses.
Michael Yang
3

Je crois en clair JSet avec ECMAScript 2015, ce qui est doux et simple à comprendre.

var is_arrays_compare_similar = function (array1, array2) {

    let flag = true;

    if (array1.length == array2.length) {

        // check first array1 object is available in array2 index
        array1.every( array_obj => {
            if (flag) {
                if (!array2.includes(array_obj)) {
                    flag = false;
                }
            }
        });

        // then vice versa check array2 object is available in array1 index
        array2.every( array_obj => {
            if (flag) {
                if (!array1.includes(array_obj)) {
                    flag = false;
                }
            }
        });

        return flag;
    } else {
        return false;
    }

}

j'espère que cela aidera quelqu'un.

ArifMustafa
la source
1
Pourquoi la vérification vice versa est-elle nécessaire? Nous savons que les tableaux ont la même taille, donc si chaque élément du tableau 1 se trouve également dans le tableau 2; pourquoi devrions-nous alors vérifier que chaque élément de array2 est également dans array1?
JeffryHouser
2

Extension de l'idée de Tomáš Zato. Le tableau Array.prototype.compare de Tomas doit être en fait appelé Array.prototype.compareIdentical.

Il transmet:

[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 2]]) === false;
[1, "2,3"].compareIdentical ([1, 2, 3]) === false;
[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].compareIdentical ([1, 2, 1, 2]) === true;

Mais échoue:

[[1, 2, [3, 2]],1, 2, [3, 2]].compareIdentical([1, 2, [3, 2],[1, 2, [3, 2]]])

Voici la meilleure version (à mon avis):

Array.prototype.compare = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time
    if (this.length != array.length)
        return false;

    this.sort();
    array.sort();
    for (var i = 0; i < this.length; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].compare(array[i]))
                return false;
        }
        else if (this[i] != array[i]) {
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;
        }
    }
    return true;
}

http://jsfiddle.net/igos/bcfCY/

Igor S.
la source
2
-1. S'il "échoue" sur l'exemple que vous avez donné, alors ce n'est que le cas pour une définition quelque peu arbitraire de "échoue". Pourquoi vous attendriez-vous à ce que ces deux tableaux différents soient considérés comme égaux? Vous n'avez même pas expliqué quel concept `` d'égalité '' vous essayez de mettre en œuvre ici, ni pourquoi c'est un concept sensé ou utile, mais il semble que vous souhaitiez que les tableaux multidimensionnels soient comparés comme s'ils étaient réduits à unidimensionnels. ceux. Si c'est le cas, vous n'y êtes même pas parvenu: [1,2] .compare ([[1,2]]) donne faux avec votre version, tout comme avec Tomáš.
Mark Amery
Sur la base de ce que j'ai pu déduire, il dit que [1, 2, 3, 4] et [1, 3, 2, 4] doivent être comparés de manière égale (l'ordre n'a pas d'importance).
Gautham Badhrinathan
2
var a1 = [1,2,3,6];
var a2 = [1,2,3,5];

function check(a, b) {
  return (a.length != b.length) ? false : 
  a.every(function(row, index) {
    return a[index] == b[index];
  });
}  

check(a1, a2);

////// OU ///////

var a1 = [1,2,3,6];
var a2 = [1,2,3,6];

function check(a, b) {
  return (a.length != b.length) ? false : 
  !(a.some(function(row, index) {
    return a[index] != b[index];
  }));
}  

check(a1, a2)
Vasanth
la source
Vous pouvez également utiliser une fonction qui ne sera pas complètement répétée si nous obtenons la condition requise satisfaite, comme ci
Vasanth
2

Une autre approche avec très peu de code (en utilisant Array Reduce et Array includes ):

arr1.length == arr2.length && arr1.reduce((a, b) => a && arr2.includes(b), true)

Si vous voulez comparer également l'égalité de commande:

arr1.length == arr2.length && arr1.reduce((a, b, i) => a && arr2[i], true)
  • La lengthvérification garantit que l'ensemble des éléments d'un tableau n'est pas seulement un sous-ensemble de l'autre.

  • Le réducteur est utilisé pour parcourir un tableau et rechercher chaque élément dans un autre tableau. Si un élément n'est pas trouvé, la fonction de réduction revient false.

    1. Dans le premier exemple, il est testé qu'un élément est inclus
    2. Le deuxième exemple vérifie également la commande
DEls
la source
1
pourriez-vous expliquer un peu votre code pour rendre cette réponse plus claire?
ted
1. comparer les longueurs de tableau pour vous assurer qu'un tableau n'est pas un sous-ensemble de l'autre
DEls
2. utilisez le réducteur pour parcourir un tableau et recherchez chaque élément dans un autre tableau. Si un élément n'est pas trouvé, la fonction de réduction renvoie 'false'
DEls
@DEls: a modifié votre explication dans la réponse (légèrement reformulée et étendue). Vous pouvez maintenant supprimer vos commentaires et signaler le premier commentaire et celui-ci comme obsolète.
try-catch-finally
2

Une approche simple:

function equals(a, b) {
    if ((a && !b) || (!a && b) || (!a && !b) || (a.length !== b.length)) {
        return false;
    }

    var isDifferent = a.some(function (element, index) { 
        return element !== b[index];
    });

    return !isDifferent;
}
Pedro Rodrigues
la source
2

Déjà de bonnes réponses, mais je voudrais partager une autre idée qui s'est révélée fiable pour comparer les tableaux. Nous pouvons comparer deux tableaux à l'aide de JSON.stringify () . Il créera une chaîne hors du tableau et comparera ainsi deux chaînes obtenues à partir de deux tableaux pour l'égalité

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:1},2]) //true

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},2]) //false

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},[3,4],2]) //false

JSON.stringify([1,{a:1},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //false

JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //true

JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false

JSON.stringify([1,{a:2},[3,4,[4]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false

JSON.stringify([1,{a:2},[3,4,[5]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //true
AL-zami
la source
2

Récursive et travaux sur IMBRIQUÉS tableaux:

function ArrEQ(a1,a2){
   return( 
        //:Are both elements arrays?
        Array.isArray(a1)&&Array.isArray(a2) 
        ?
        //:Yes: Test each entry for equality:
        a1.every((v,i)=>(ArrEQ(v,a2[i])))
        :
        //:No: Simple Comparison:
        (a1===a2)
   );;
};;

console.log( "Works With Nested Arrays:" );
console.log( ArrEQ( 
    [1,2,3,[4,5,[6,"SAME/IDENTICAL"]]],
    [1,2,3,[4,5,[6,"SAME/IDENTICAL"]]]
));;     
console.log( ArrEQ( 
    [1,2,3,[4,5,[6,"DIFFERENT:APPLES" ]]],
    [1,2,3,[4,5,[6,"DIFFERENT:ORANGES"]]]
));;  
JMI MADISON
la source
2

Fonctionne avec de MULTIPLES arguments avec des tableaux NESTED :

//:Return true if all of the arrays equal.
//:Works with nested arrays.
function AllArrEQ(...arrays){
    for(var i = 0; i < (arrays.length-1); i++ ){
        var a1 = arrays[i+0];
        var a2 = arrays[i+1];
        var res =( 
            //:Are both elements arrays?
            Array.isArray(a1)&&Array.isArray(a2) 
            ?
            //:Yes: Compare Each Sub-Array:
            //:v==a1[i]
            a1.every((v,i)=>(AllArrEQ(v,a2[i])))
            :
            //:No: Simple Comparison:
            (a1===a2)
        );;
        if(!res){return false;}
    };;
    return( true );
};;

console.log( AllArrEQ( 
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
));; 
JMI MADISON
la source
2
In a simple way uning stringify but at same time thinking in complex arrays:

**Simple arrays**:  
var a = [1,2,3,4];  
var b = [4,2,1,4];  
JSON.stringify(a.sort()) === JSON.stringify(b.sort()) // true  

**Complex arrays**:  
var a = [{id:5,name:'as'},{id:2,name:'bes'}];  
var b = [{id:2,name:'bes'},{id:5,name:'as'}];  
JSON.stringify(a.sort(function(a,b) {return a.id - b.id})) === JSON.stringify(b.sort(function(a,b) {return a.id - b.id})) // true  

**Or we can create a sort function**  

function sortX(a,b) {  
return a.id -b.id; //change for the necessary rules  
}  
JSON.stringify(a.sort(sortX)) === JSON.stringify(b.sort(sortX)) // true  
Pedro Bustamante
la source