1177 votes

Comment comparer des tableaux en JavaScript ?

J'aimerais comparer deux tableaux... idéalement, de manière efficace. Rien d'extraordinaire, juste true s'ils sont identiques, et false si non. 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 des tableaux sans avoir à itérer sur chaque valeur ?

6 votes

Vous pourriez d'abord comparer leur longueur, et si elles sont égales, chaque valeur.

63 votes

Qu'est-ce qui rend deux tableaux égaux pour vous ? Les mêmes éléments ? Le même ordre des éléments ? L'encodage en JSON ne fonctionne que si l'élément du tableau peut être sérialisé en JSON. Si le tableau peut contenir des objets, jusqu'où pouvez-vous aller ? Quand deux objets sont-ils "égaux" ?

0 votes

JSON.parse itérerait également à travers chaque valeur de toute façon, donc je suppose qu'il serait mieux de comparer l'itération à travers chaque valeur et de réduire certaines étapes d'exécution (comme l'encodage en JSON).

946voto

Tomáš Zato Points 4901

Pour comparer des tableaux, il faut les parcourir en boucle et comparer chaque valeur :

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

Utilisation :

[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 de caractères - sans boucles... "Eh bien, vous devriez noter qu'il y a des boucles. La première boucle récursive qui convertit le tableau en chaîne de caractères et la seconde, qui compare deux chaînes de caractères. Donc cette méthode est plus rapide que l'utilisation de la chaîne de caractères .

Je pense que les grandes quantités de données devraient toujours être stockées dans des tableaux, et non dans des objets. Cependant, si vous utilisez des objets, ils peuvent aussi être partiellement comparés.<br><strong>Voici comment :</strong>

Comparer des objets :

J'ai dit plus haut, que deux objets instances ne seront jamais égales, même si elles contiennent les mêmes données à ce moment-là :

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

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

Toutefois, si vous utilisez uniquement la structure d'objet pour contenir les 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;
}  

Toutefois, n'oubliez pas que celle-ci doit servir à comparer des données de type JSON, et non des instances de classe ou d'autres éléments. Si vous voulez comparer des objets plus compliqués, regardez à cette réponse et sa fonction superlongue .
Pour que cela fonctionne avec Array.equals vous devez modifier un peu la fonction originale :

...
    // 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 indexOf y contains

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

3 votes

Merci pour votre contribution. Compte tenu du nombre de visites, je suppose que de nombreux utilisateurs redirigés ici depuis Google sont confrontés à la même énigme, à savoir comment comparer "correctement" deux tableaux !

30 votes

Si vous voulez faire des comparaisons strictes, utilisez this[i] !== array[i] au lieu de != .

38 votes

Votre méthode doit être appelée equals au lieu de compare . Au moins dans .NET, comparer renvoie généralement un entier signé indiquant quel objet est plus grand que l'autre. Voir : Comparer.Comparer .

243voto

Jason Boerner Points 304

J'aime utiliser la bibliothèque Underscore pour les projets de codage lourds en 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

29 votes

Notez que l'ordre importe _.isEqual([1,2,3], [2,1,3]) => false

3 votes

Ou si vous voulez juste le isEqual vous pouvez toujours utiliser le module lodash.isequal

6 votes

Vous pouvez peut-être utiliser _.difference() ; si l'ordre n'a pas d'importance pour vous.

61voto

Tim Down Points 124501

Ce que vous entendez par "identique" n'est pas clair. Par exemple, est-ce que les tableaux a y b ci-dessous sont identiques (notez les tableaux imbriqués) ?

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

Voici une fonction optimisée de comparaison de tableaux qui compare les éléments correspondants de chaque tableau à tour de rôle en utilisant l'égalité stricte et ne fait pas de comparaison récursive des éléments de tableaux qui sont eux-mêmes des tableaux, ce qui signifie que pour l'exemple ci-dessus, arraysIdentical(a, b) rendrait false . Il fonctionne dans le cas général, que les fichiers JSON et join() -Les solutions basées sur la technologie ne le feront 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;
};

0 votes

@ASDF : La question ne précise pas clairement ce que signifie "identique". De toute évidence, cette réponse ne fait qu'une vérification superficielle. Je vais ajouter une note.

0 votes

Cela échoue pour arraysIdentical([1, 2, [3, 2]], [1, 2, [3, 2]]) ;

4 votes

@GopinathShiva : Eh bien, il n'échoue que si vous vous attendez à ce qu'il revienne. true . La réponse explique que ce n'est pas le cas. Si vous avez besoin de comparer des tableaux imbriqués, vous pouvez facilement ajouter une vérification récursive.

42voto

Zack Marrapese Points 7866

Vous pourriez faire a2.toString() == a1.toString() .

Cela donnerait quelque chose comme :

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

var a1String = a1.toString() // "1,2,3"
var a2String = a2.toString() // "1,2,3"

a1String == a2String // true

28voto

Dans le prolongement de la réponse de Tomáš Zato, je suis d'accord pour dire que l'itération dans les tableaux est la plus rapide. De plus (comme d'autres l'ont déjà dit), la fonction devrait être appelée equals/equal, et non compare. À la lumière de ces éléments, j'ai modifié la fonction pour gérer la comparaison des tableaux pour la similarité - c'est-à-dire qu'ils ont les mêmes éléments, mais dans un ordre différent - pour un usage personnel, et j'ai pensé le mettre ici pour que tout le monde puisse le 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 de strict qui a la valeur par défaut de true. Ce paramètre strict définit si les tableaux doivent être totalement égaux en termes de contenu et d'ordre de ce contenu, ou s'ils doivent simplement contenir le même contenu.

Ejemplo:

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 aussi écrit un petit jsfiddle avec la fonction et cet exemple :
http://jsfiddle.net/Roundaround/DLkxX/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X