2020 votes

Suppression des valeurs en double dans un tableau JS

J'ai un tableau JavaScript très simple qui peut ou non contenir des doublons.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

Je dois supprimer les doublons et placer les valeurs uniques dans un nouveau tableau.

Je pourrais indiquer tous les codes que j'ai essayés mais je pense que c'est inutile car ils ne fonctionnent pas. J'accepte aussi les solutions jQuery.

Question similaire :

87 votes

_.uniq(peoplenames) résout ce problème lodash.com/docs#uniq

10 votes

@ConnorLeech c'est facile avec lodash mais pas de manière optimisée.

0 votes

La meilleure solution est de convertir un tableau en objet, les clés de l'objet étant des éléments du tableau, la valeur de chaque clé étant par exemple "true". Ensuite, il suffit de reconvertir le tableau avec Object.keys(obj_name).

5256voto

georg Points 52691

Manière maline mais naïve uniqueArray = myArray.filter(function(item, pos) { return myArray.indexOf(item) == pos; })


Fondamentalement, nous allons parcourir le tableau et, pour chaque élément, vérifiez si la première position de cet élément dans la matrice est égale à la position actuelle. Évidemment, ces deux positions sont différentes pour les éléments en double.

À l'aide du 3e ("array") paramètre du filtre de rappel, nous pouvons éviter une fermeture de la variable du tableau:

uniqueArray = myArray.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

Bien que concis, cet algorithme n'est pas particulièrement efficace pour les grands tableaux (quadratique du temps).

Les tables de hashage à la rescousse

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

C'est ce qui se fait habituellement. L'idée est de placer chaque élément dans une table de hachage, puis de vérifier sa présence instantanément. Cela nous fait gagner du temps, mais a au moins deux inconvénients:

  • comme les clés de hachage ne peuvent être des chaînes de caractères en Javascript, ce code ne fait pas la distinction des nombres et des "chaînes numériques". C'est, uniq([1,"1"]) sera de nouveau juste [1]
  • pour la même raison, tous les objets seront considérés comme égaux: uniq([{foo:1},{foo:2}]) sera de nouveau juste [{foo:1}].

Cela dit, si vos tableaux contiennent uniquement des types primaires et vous n'avez pas de soins sur les types (par exemple, c'est toujours des nombres), cette solution est optimale.

Le meilleur de deux mondes

Une solution universelle combine les deux approches: il utilise le hachage des recherches pour les types primaires et la recherche linéaire pour les objets.

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

sort | uniq

Une autre option est de trier le tableau en premier, puis de retirez chaque élément égal au précédent:

function uniq(a) {
    return a.sort().filter(function(item, pos) {
        return !pos || item != a[pos - 1];
    })
}

Encore une fois, cela ne fonctionne pas avec des objets (car tous les objets sont égaux avec sort). En outre, cette méthode silencieusement les modifications du tableau original comme un effet secondaire - pas bon!!!

Unique par...

Parfois, il est souhaité de uniquify une liste basée sur des critères autres que la simple égalité, par exemple, pour trier des objets qui sont différents, mais qui partagent certaines propriétés. Cela peut être fait avec élégance par le passage d'un rappel, habituellement appelé une "clé". key() qui est appliquée à chaque élément, et d'éléments égaux "clés" sont supprimés. Depuis key retour est prévu un primitif, table de hachage, beau travail ici:

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

Particulièrement utile key() est JSON.stringify qui permet de filtrer les objets à l'identique de la structure:

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

15 votes

Filter et indexOf ont été introduits dans ECMAScript 5, donc cela ne fonctionnera pas dans les anciennes versions d'IE (<9). Si vous tenez à ces navigateurs, vous devrez utiliser des bibliothèques avec des fonctions similaires (jQuery, underscore.js etc.).

14 votes

@RoderickObrist vous pouvez le faire si vous voulez que votre page fonctionne dans les anciens navigateurs.

0 votes

Pourquoi ne pas utiliser la référence au tableau fournie par la méthode du filtre pour rendre la réponse plus générale ?

509voto

Roman Bataev Points 2806

Rapide et sale en utilisant jQuery :

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});

359 votes

Je ne serais pas contre une réponse non-jquery pour ceux qui ne l'utilisent pas.

8 votes

Comme cela a été rétabli dans l'original inArray par une personne réputée, je vais de nouveau mentionner : cette solution est O(n^2), ce qui la rend inefficace.

40 votes

J'aimerais vraiment qu'en 2020 nous puissions commencer à déprécier jQuery et d'autres réponses encore plus datées... Stackoverflow commence à montrer un certain âge ici...

374voto

Christian Landgren Points 1127

Suis fatigué de voir tous les mauvais exemples avec des boucles ou jQuery. Javascript a les outils parfaits pour cela de nos jours: le tri, la carte et la réduction.

Uniq réduire tout en gardant ordre existant

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

Plus rapide uniq avec tri

Il y a probablement des façons plus rapides, mais celui-ci est assez décent.

var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a - b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a - b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

5 votes

C'est parfait car, contrairement au filtre, il permet de manipuler les objets en profondeur.

4 votes

Cette réponse mérite plus de votes positifs. Juste magnifique, et seulement la solution Javascript comme demandé par OP ! Merci !

4 votes

Réponse parfaite, propre et fonctionnelle.

154voto

Darthfett Points 1645

Vanilla JS : Supprimer les doublons en utilisant un objet comme un ensemble

Vous pouvez toujours essayer de le mettre dans un objet, puis d'itérer à travers ses clés :

function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

Vanilla JS : Suppression des doublons en suivant les valeurs déjà vues (order-safe)

Ou, pour une version sécurisée, utilisez un objet pour stocker toutes les valeurs vues précédemment, et vérifiez les valeurs par rapport à cet objet avant de les ajouter à un tableau.

function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6 : Utilisation de la nouvelle structure de données Set (order-safe)

ECMAScript 6 ajoute le nouveau Set Structure de données, qui permet de stocker des valeurs de tout type. Set.values renvoie les éléments dans l'ordre d'insertion.

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

Exemple d'utilisation :

a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

7 votes

Dans les navigateurs plus récents, vous pourriez même faire var c = Object.keys(b) . Il convient de noter que cette approche ne fonctionne que pour les chaînes de caractères, mais ce n'est pas grave, c'est ce que demandait la question initiale.

1 votes

Il convient également de noter que vous pouvez perdre l'ordre du tableau, car les objets ne conservent pas leurs propriétés dans l'ordre.

1 votes

@JuanMendes J'ai créé une version sécurisée, qui copie simplement dans le nouveau tableau si la valeur n'a pas été vue auparavant.

75voto

Brandon Boone Points 8372

Utilisez Underscore.js

Il s'agit d'une bibliothèque contenant une multitude de fonctions permettant de manipuler des tableaux.

C'est la cravate qui va avec le smoking de JQuery et les bretelles de Backbone.js. de Backbone.js.

_.uniq

_.uniq(array, [isSorted], [iterator]) Alias : unique
Produit une version sans duplicata du fichier tableau en utilisant === pour tester l'égalité l'égalité. Si vous savez à l'avance que le tableau est trié, en passant vrai pour isSorted exécutera un algorithme beaucoup plus rapide. Si vous voulez calculer des éléments uniques basés sur une transformation, passez un fichier itérateur fonction.

Exemple

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

alert(_.uniq(names, false));

Note : Lo-Dash (un soulignement concurrent) offre également un .uniq mise en œuvre.

2 votes

Malheureusement, underscore ne permet pas de définir une fonction d'égalité personnalisée. Le rappel qu'il autorise est pour une fonction 'itérée', par exemple avec des arguments (élément, valeur, tableau).

0 votes

[...new Set(Array)] est plus qu'un compagnon suffisant

1 votes

@norbekoff - absolument, lol. ~10 ans plus tard !

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