Obtenez toutes les valeurs uniques dans un tableau JavaScript (supprimez les doublons)

1488

J'ai un tableau de nombres dont je dois m'assurer qu'ils sont uniques. J'ai trouvé l'extrait de code ci-dessous sur Internet et cela fonctionne très bien jusqu'à ce que le tableau contienne un zéro. J'ai trouvé cet autre script ici sur Stack Overflow qui lui ressemble presque exactement, mais il n'échoue pas.

Donc, pour m'aider à apprendre, quelqu'un peut-il m'aider à déterminer où le script prototype va mal?

Array.prototype.getUnique = function() {
 var o = {}, a = [], i, e;
 for (i = 0; e = this[i]; i++) {o[e] = 1};
 for (e in o) {a.push (e)};
 return a;
}

Plus de réponses à la question en double:

Question similaire:

Mottie
la source
3
@hippietrail Cette question plus ancienne concerne la recherche et le retour uniquement des doublons (j'étais confus aussi!). Ma question est plutôt de savoir pourquoi cette fonction échoue lorsqu'un tableau contient un zéro.
Mottie
Vous voulez probablement aussi rendre le titre de votre question moins vague.
hippietrail
Pour les futurs lecteurs, lorsque vous commencez à découvrir que vous devez constamment modifier algorithmiquement le contenu de votre structure de données (les ordonner, supprimer les éléments répétitifs, etc.) ou rechercher des éléments à l'intérieur à chaque itération, il est prudent de supposer que vous utilisez la mauvaise structure de données en premier lieu et commencez à en utiliser une plus appropriée pour la tâche à accomplir (dans ce cas, un ensemble de hachage au lieu d'un tableau).
nurettin
J'ai copié le code ailleurs, il y a très longtemps ... mais il semble assez simple: o= object, a= array, i= indexet e= umm, quelque chose: P
Mottie

Réponses:

2661

Avec JavaScript 1.6 / ECMAScript 5, vous pouvez utiliser la filterméthode native d'un tableau de la manière suivante pour obtenir un tableau avec des valeurs uniques:

function onlyUnique(value, index, self) { 
    return self.indexOf(value) === index;
}

// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

La méthode native filterparcourra le tableau et ne laissera que les entrées qui passent la fonction de rappel donnée onlyUnique.

onlyUniquevérifie si la valeur donnée est la première survenue. Sinon, il doit s'agir d'un doublon et ne sera pas copié.

Cette solution fonctionne sans bibliothèque supplémentaire comme jQuery ou prototype.js.

Cela fonctionne également pour les tableaux avec des types de valeurs mixtes.

Pour les anciens navigateurs (<ie9), qui ne prennent pas en charge les méthodes natives filteret indexOfvous pouvez trouver des solutions dans la documentation MDN pour filter et indexOf .

Si vous souhaitez conserver la dernière occurrence d'une valeur, remplacez simplement indexOfpar lastIndexOf.

Avec ES6, cela pourrait être raccourci:

// usage example:
var myArray = ['a', 1, 'a', 2, '1'];
var unique = myArray.filter((v, i, a) => a.indexOf(v) === i); 

// unique is ['a', 1, 2, '1']

Merci à Camilo Martin pour un indice en commentaire.

ES6 possède un objet natif Setpour stocker des valeurs uniques. Pour obtenir un tableau avec des valeurs uniques, vous pouvez maintenant faire ceci:

var myArray = ['a', 1, 'a', 2, '1'];

let unique = [...new Set(myArray)]; 

// unique is ['a', 1, 2, '1']

Le constructeur de Setprend un objet itérable, comme Array, et l'opérateur de propagation ...transforme l'ensemble en un tableau. Merci à Lukas Liese pour un indice en commentaire.

TLindig
la source
55
Cette solution sera malheureusement beaucoup plus lente. Vous bouclez deux fois, une fois avec filtre et une fois avec index de
Jack Franzen
19
@JackFranzen Plus lent que quoi? La solution de Rafael ? La solution Rafaels ne fonctionne pas pour les tableaux de type mixte. Pour mon exemple, ['a', 1, 'a', 2, '1']vous obtiendrez ['a', 1, 2]. Mais ce n'est pas ce que j'attendais. BTW, beaucoup plus lent est très relatif.
TLindig
25
Dans JS moderne: .filter((v,i,a)=>a.indexOf(v)==i)(notation de flèche grasse).
Camilo Martin
164
let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Lukas Liesis
5
Pour une réponse beaucoup plus détaillée, y compris de nombreuses possibilités - telles que le tri en premier et la gestion de différents types de données - voir stackoverflow.com/a/9229821/368896
Dan Nissenbaum
877

Réponse mise à jour pour ES6 / ES2015 : à l'aide de l' ensemble , la solution unifilaire est:

var items = [4,5,4,6,3,4,5,2,23,1,4,4,4]
var uniqueItems = Array.from(new Set(items))

Qui revient

[4, 5, 6, 3, 2, 23, 1]

Comme suggéré par le_m , cela peut également être raccourci à l'aide de l' opérateur d'étalement , comme

var uniqueItems = [...new Set(items)]
À
la source
11
Remarquez que ce réseau interne ne fonctionnerait pasArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov
3
Performance de cette solution par rapport à myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw
43
Veuillez noter que si vous utilisez le Setet ajoutez des objets au lieu de valeurs primitives, il contiendra des références uniques aux objets. Ainsi, l'ensemble sdans let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);renverra ceci: Set { { Foo: 'Bar' }, { Foo: 'Bar' } }qui est un Setavec des références d'objet uniques aux objets qui contiennent les mêmes valeurs. Si vous écrivez let o = {Foo:"Bar"};puis créez un ensemble avec deux références comme ceci let s2 = new Set([o,o]);Set { { Foo: 'Bar' } }
:,
2
Ces deux options ont des problèmes sur IE10 même avec une couche polyfill.io présente
Thymine
2
le nouveau scénario de test jsperf.com/array-filter-unique-vs-new-set/1 ressemble new Setau trophée de
shuk
167

Je me rends compte que cette question a déjà plus de 30 réponses. Mais j'ai d'abord lu toutes les réponses existantes et fait mes propres recherches.

J'ai divisé toutes les réponses en 4 solutions possibles:

  1. Utilisez la nouvelle fonctionnalité ES6: [...new Set( [1, 1, 2] )];
  2. Utiliser un objet { }pour éviter les doublons
  3. Utiliser un tableau d'assistance [ ]
  4. Utilisation filter + indexOf

Voici des exemples de codes trouvés dans les réponses:

Utilisez la nouvelle fonctionnalité ES6: [...new Set( [1, 1, 2] )];

function uniqueArray0(array) {
  var result = Array.from(new Set(array));
  return result    
}

Utiliser un objet { }pour éviter les doublons

function uniqueArray1( ar ) {
  var j = {};

  ar.forEach( function(v) {
    j[v+ '::' + typeof v] = v;
  });

  return Object.keys(j).map(function(v){
    return j[v];
  });
} 

Utiliser un tableau d'assistance [ ]

function uniqueArray2(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Utilisation filter + indexOf

function uniqueArray3(a) {
  function onlyUnique(value, index, self) { 
      return self.indexOf(value) === index;
  }

  // usage
  var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

  return unique;
}

Et je me suis demandé laquelle est la plus rapide. J'ai créé un exemple de feuille Google pour tester les fonctions. Remarque: ECMA 6 n'est pas disponible dans Google Sheets, je ne peux donc pas le tester.

Voici le résultat des tests: entrez la description de l'image ici

Je m'attendais à voir que le code utilisant l'objet { }gagnera parce qu'il utilise du hachage. Je suis donc heureux que les tests aient montré les meilleurs résultats pour cet algorithme dans Chrome et IE. Merci à @rab pour le code .

Max Makhrov
la source
L'option "filter + indexOf" est extrêmement lente sur les tableaux de plus de 100 000 éléments. J'ai dû utiliser l'approche "object map" mais elle casse le tri d'origine.
liberborn
Un nombre plus élevé est-il plus lent, pas plus rapide?
fletchsod
3
@ fletchsod, les nombres sont le temps en ms pour exécuter le code.
Max Makhrov
1
Les chiffres sont mauvais! Le script des applications Google s'exécute sur leur serveur, pas sur le navigateur du client - et donc les performances sur Chrome / IE (ou pratiquement n'importe quel navigateur) seront les mêmes!
Jay Dadhania
1
Bien qu'il soit possible d'exécuter JS côté client à partir de Google Script (via google.script.host ou google.script.run - je ne sais pas lequel), la réponse indique clairement que "ECMA 6 n'est pas disponible dans Google Sheets" - donc nous pouvons supposer en toute sécurité que l'affiche a utilisé Google Script côté serveur pour cela - et donc la réponse montre les performances du serveur Google, pas du navigateur !!
Jay Dadhania
134

Vous pouvez également utiliser underscore.js .

console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>

qui retournera:

[1, 2, 3, 4]
kornfridge
la source
22
Veuillez faire ceci les gens. Ne connectez pas quelque chose au prototype Array. S'il vous plaît.
Jacob Dalton
5
@JacobDalton - Ce n'est pas l'extension du prototype Array. Il est espacé dans l'objet _.
superluminaire
25
@superluminary Je sais que c'est pourquoi j'ai dit s'il vous plaît faites ceci. La solution acceptée suggère de modifier le prototype Array. Ne fais pas ça.
Jacob Dalton
21
@JacobDalton Veuillez ne pas faire cela. Il n'est pas nécessaire d'ajouter une bibliothèque supplémentaire uniquement pour un petit travail qui peut être fait avecarray = [...new Set(array)]
3
Cela a contrecarré l'utilisation des fonctionnalités ES et l'ajout de bibliothèques ne fait que gonfler le site Web.
fletchsod
68

One Liner, Pure JavaScript

Avec la syntaxe ES6

list = list.filter((x, i, a) => a.indexOf(x) == i)

x --> item in array
i --> index of item
a --> array reference, (in this case "list")

entrez la description de l'image ici

Avec la syntaxe ES5

list = list.filter(function (x, i, a) { 
    return a.indexOf(x) == i; 
});

Compatibilité du navigateur : IE9 +

Vamsi
la source
14
@Spets Il a un coût quadratique, donc ce n'est pas la meilleure réponse
Oriol
@Spets comme tous uniques / distincts ... oui, soyez intelligent, utilisez radix pour trier d'abord, soyez plus lent que 0 (n2) recherche rapide dans la plupart des cas.
doker
Merci les gars! Je ne l'avais pas réalisé quand j'ai regardé le code pour la première fois, mais maintenant c'est un peu plus évident.
Spets
51

J'ai depuis trouvé une belle méthode qui utilise jQuery

arr = $.grep(arr, function(v, k){
    return $.inArray(v ,arr) === k;
});

Remarque: Ce code a été tiré du poste de frappe de canard de Paul Irish - j'ai oublié de donner le crédit: P

Mottie
la source
8
Une solution concise, mais appeler inArray est beaucoup moins efficace que d'appeler hasOwnProperty.
Monsieur Smith
C'est aussi O (N ^ 2), non? Alors que l'approche dictionnaire ou hasOwnProperty serait probablement O (N * logN).
speedplane
Cela a fonctionné pour moi, toutes les autres solutions n'étaient pas prises en charge par Internet Explorer.
kerl
51

Solution la plus courte avec ES6: [...new Set( [1, 1, 2] )];

Ou si vous souhaitez modifier le prototype Array (comme dans la question d'origine):

Array.prototype.getUnique = function() {
    return [...new Set( [this] )];
};

EcmaScript 6 n'est implémenté que partiellement dans les navigateurs modernes pour le moment (août 2015), mais Babel est devenu très populaire pour repasser ES6 (et même ES7) vers ES5. De cette façon, vous pouvez écrire du code ES6 aujourd'hui!

Si vous vous demandez ce que cela ...signifie, cela s'appelle l' opérateur de propagation . D'après MDN : «L'opérateur d'étalement permet de développer une expression dans des endroits où plusieurs arguments (pour les appels de fonction) ou plusieurs éléments (pour les littéraux de tableau) sont attendus». Parce qu'un ensemble est un itérable (et ne peut avoir que des valeurs uniques), l'opérateur de propagation étendra l'ensemble pour remplir le tableau.

Ressources pour apprendre ES6:

Torsten Becker
la source
3
vous pouvez le faire encore plus court, avec a = [...Set(a)], mais, de toute façon, ce n'est que Firefox, pour l'instant.
c69
@ c69, à droite, ne sera pas plus court que ça. Les utilisateurs de SpiderMonkey apprécieront également.
Torsten Becker
Fonctionne avec Babel. Il vous suffit d'inclure également la cale Array.from:require ( "core-js/fn/array/from" );
Vladislav Rastrusny
devrait-il être Array.prototype.getUnique = function () {return [... new Set ([this])]; }; ou, Array.prototype.getUnique = function () {return [... new Set (this)]; }; Je l'ai appliqué sur les éléments suivants: $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9. _-] + \. [a-zA-Z0-9 ._-] +) / gi) .getUnique (); Le premier m'a renvoyé une valeur unique uniquement lorsque le second a réajusté tous les doublons supprimés.
ashique
[...Set(['a', 1, 'a', 2, '1'])]va lancer une TypeError, il est donc toujours sage de conserver new:[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz
36

Solution la plus simple:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log([...new Set(arr)]);

Ou:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log(Array.from(new Set(arr)));

Pedro L.
la source
4
Notez que cela n'est pas pris en charge sur IE10 et ci-dessous. Notez qu'IE11 + et Safari offrent un support limité (même pas sûr que l'exemple ci-dessus fonctionne) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
oriadam
32

La façon la plus simple et la plus rapide (dans Chrome) de procéder:

Array.prototype.unique = function() {
    var a = [];
    for (var i=0, l=this.length; i<l; i++)
        if (a.indexOf(this[i]) === -1)
            a.push(this[i]);
    return a;
}

Parcoure simplement chaque élément du tableau, teste si cet élément est déjà dans la liste, et si ce n'est pas le cas, poussez vers le tableau qui est retourné.

Selon jsPerf, cette fonction est la plus rapide de celles que j'ai pu trouver n'importe où - n'hésitez pas à ajouter la vôtre.

La version non prototype:

function uniques(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Tri

Lorsque vous devez également trier le tableau, ce qui suit est le plus rapide:

Array.prototype.sortUnique = function() {
    this.sort();
    var last_i;
    for (var i=0;i<this.length;i++)
        if ((last_i = this.lastIndexOf(this[i])) !== i)
            this.splice(i+1, last_i-i);
    return this;
}

ou non prototype:

function sortUnique(arr) {
    arr.sort();
    var last_i;
    for (var i=0;i<arr.length;i++)
        if ((last_i = arr.lastIndexOf(arr[i])) !== i)
            arr.splice(i+1, last_i-i);
    return arr;
}

C'est également plus rapide que la méthode ci-dessus dans la plupart des navigateurs non-chrome.

Joeytje50
la source
Sous Linux, Chrome 55.0.2883 préfère votre arr.unique () et arrclone2.sortFilter () de swilliams est le plus lent (78% plus lent). Cependant, Firefox 51.0.0 (avec beaucoup d'addons) a swilliams comme le plus rapide (mais toujours plus lent d'Ops / sec que tout autre résultat Chrome) avec jQuery $ .grep de mottie (arr, jqFilter) étant le plus lent (46% plus lent). Votre arr.uniq () était 30% plus lent. J'ai exécuté chaque test deux fois et j'ai obtenu des résultats cohérents. L' arr.getUnique () de Rafael a obtenu la deuxième place dans les deux navigateurs.
Adam Katz
jsPerf est bogué au moment, donc mon modifier à ce test n'a pas commis tout, mais il a fait en ajoutant deux tests: Cocco toUnique () instrus Vamsi ES6 de list.filter () sur les deux navigateurs, en battant sortFilter de swilliams () pour # 1 sur FF (sortFilter était 16% plus lent) et battre votre test trié (qui était plus lent de 2%) pour # 3 sur Chrome.
Adam Katz
Ah, je n'avais pas compris que ces tests étaient trivialement petits et n'avaient pas vraiment d'importance. Un commentaire à la réponse acceptée décrit ce problème et propose une correction dans une révision du test, dans lequel le code de Rafael est facilement le plus rapide et le code arr.unique de Joetje50 est 98% plus lent. J'ai également fait une autre révision comme indiqué dans ce commentaire .
Adam Katz
1
Eh bien, en fait, l'algorithme que vous avez implémenté dans la uniquefonction a une complexité O (n ^ 2) tandis que celui dans getUniqueest O (n). Le premier peut être plus rapide sur de petits ensembles de données, mais comment pouvez-vous discuter avec les mathématiques :) Vous pouvez vous assurer que le dernier est plus rapide si vous l'exécutez sur un tableau, disons, d'1e5 éléments uniques
Mikhail Dudin
31

PERFORMANCE UNIQUEMENT! ce code est probablement 10 fois plus rapide que tous les codes ici * fonctionne sur tous les navigateurs et a également l'impact mémoire le plus faible .... et plus

si vous n'avez pas besoin de réutiliser l'ancien tableau; btw effectuer les autres opérations nécessaires avant de le convertir en unique ici est probablement le moyen le plus rapide de le faire, également très court.

var array=[1,2,3,4,5,6,7,8,9,0,1,2,1];

alors vous pouvez essayer ceci

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1];

function toUnique(a, b, c) { //array,placeholder,placeholder
  b = a.length;
  while (c = --b)
    while (c--) a[b] !== a[c] || a.splice(c, 1);
  return a // not needed ;)
}
console.log(toUnique(array));
//[3, 4, 5, 6, 7, 8, 9, 0, 2, 1]

Je suis venu avec cette fonction en lisant cet article ...

http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/

Je n'aime pas la boucle for. il a de nombreux paramètres.j'aime la boucle while--. while est la boucle la plus rapide de tous les navigateurs, à l'exception de celle que nous aimons tous tellement ... chrome.

de toute façon j'ai écrit la première fonction qui utilise while.Et oui c'est un peu plus rapide que la fonction trouvée dans l'article.mais pas assez.unique2()

prochaine étape, utilisez js moderne. Object.keys j'ai remplacé l'autre pour la boucle par les Object.keys de js1.7 ... un peu plus rapide et plus court (en chrome 2x plus rapide);). Pas assez!. unique3().

à ce stade, je pensais à ce dont j'avais vraiment besoin dans MA fonction unique. je n'ai pas besoin de l'ancien tableau, je veux une fonction rapide. j'ai donc utilisé 2 while boucles + épissure.unique4()

Inutile de dire que j'ai été impressionné.

chrome: les 150 000 opérations par seconde habituelles sont passées à 1 800 000 opérations par seconde.

soit: 80 000 op / s vs 3 500 000 op / s

ios: 18 000 op / s contre 170 000 op / s

safari: 80 000 op / s vs 6 000 000 op / s

Preuve http://jsperf.com/wgu ou mieux utiliser console.time ... microtime ... peu importe

unique5() est juste pour vous montrer ce qui se passe si vous souhaitez conserver l'ancien tableau.

Ne l'utilisez pas Array.prototypesi vous ne savez pas ce que vous faites. je viens de faire beaucoup de copie et de passé. À utiliser Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})si vous souhaitez créer un prototype natif.exemple : https://stackoverflow.com/a/20463021/2450730

Démo http://jsfiddle.net/46S7g/

REMARQUE: votre ancienne baie est détruite / devient unique après cette opération.

si vous ne pouvez pas lire le code ci-dessus demandez, lisez un livre javascript ou voici quelques explications sur le code plus court. https://stackoverflow.com/a/21353032/2450730

certains utilisent indexOf ... ne pas ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

pour les tableaux vides

!array.length||toUnique(array); 
cocco
la source
4
testé sur node.js, avec un tableau de 100 000 URL (chaînes). Le résultat a été 2x plus lent que underscore.js _.uniq ... bien qu'un jsperf distinct soit d'accord avec vous ( jsperf.com/uniq-performance/5 ), je suis déçu :(
xShirase
3
vous ne testez pas correctement dans jsperf ... dans votre exemple vous définissez la fonction à chaque fois ... mais les fonctions underscore.js sont déjà définies .. cela pénalise ma fonction. testez également 3 et 4. une autre chose que je dois dire est que si vous utilisez des variables mixtes (chaînes et nombres), vous devez remplacer un [b]! == a [c] par un [b]! = a [c]
cocco
2
Je ne sais pas si le jsPerf a été corrigé, mais il semble que l'alternative Reduce (qui, pour moi, est plus facile à comprendre) soit 94% plus rapide que votre solution. jsperf.com/reduce-for-distinct Edit: Le vôtre est plus lent sur Chrome, le même sur Edge et plus rapide sur Firefox.
Victor Ivens
3
Utilisable uniquement avec de petits tableaux / faible pourcentage de doublons . Chaque fois qu'un objet non unique a été trouvé, le moteur sera forcé de déplacer tous les éléments restants vers la gauche, en: 1) itérant sur eux 2) les lisant 3) les écrivant au nouvel emplacement. Et puis, il crée également un nouveau tableau avec cet élément épissé, et le renvoie comme résultat ... L'algorithme se dégrade rapidement avec des tableaux plus grands / des doublons plus fréquents. Pour les matrices de taille 1000-> 10000-> 50000 et ~ 40% de doublons, le temps de prise moyen sera de 2-> 775-> 19113 ms. (la taille change comme 1-> x10-> x5, le temps comme 1-> x10-> x25) dans Chrome.
ankhzet
1
Merci. Belle approche. Mais qu'en est-il des bons articles?
liberborn
28

Beaucoup de réponses ici peuvent ne pas être utiles aux débutants. Si la déduplication d'un tableau est difficile, connaîtront-ils vraiment la chaîne du prototype, ou même jQuery?

Dans les navigateurs modernes, une solution propre et simple consiste à stocker des données dans un ensemble , conçu pour être une liste de valeurs uniques.

const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));

Le Array.fromest utile pour reconvertir l'ensemble en tableau afin que vous ayez un accès facile à toutes les méthodes (fonctionnalités) impressionnantes dont disposent les tableaux. Il existe également d' autres façons de faire la même chose. Mais vous n'aurez peut-être pas besoin Array.fromdu tout, car les ensembles ont de nombreuses fonctionnalités utiles comme forEach .

Si vous devez prendre en charge l'ancien Internet Explorer et ne pouvez donc pas utiliser Set, une technique simple consiste à copier des éléments dans un nouveau tableau tout en vérifiant au préalable s'ils sont déjà dans le nouveau tableau.

// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];

// Go through each car, one at a time.
cars.forEach(function (car) {
    // The code within the following block runs only if the
    // current car does NOT exist in the uniqueCars list
    // - a.k.a. prevent duplicates
    if (uniqueCars.indexOf(car) === -1) {
        // Since we now know we haven't seen this car before,
        // copy it to the end of the uniqueCars list.
        uniqueCars.push(car);
    }
});

Pour le rendre instantanément réutilisable, mettons-le dans une fonction.

function deduplicate(data) {
    if (data.length > 0) {
        var result = [];

        data.forEach(function (elem) {
            if (result.indexOf(elem) === -1) {
                result.push(elem);
            }
        });

        return result;
    }
}

Donc, pour nous débarrasser des doublons, nous le ferions maintenant.

var uniqueCars = deduplicate(cars);

La deduplicate(cars)partie devient la chose que nous avons appelé résultat lorsque la fonction de finalise de.

Passez-lui simplement le nom du tableau que vous aimez.

Seth Holladay
la source
Soit dit en passant, j'ai utilisé un tableau plein de chaînes pour montrer que ma technique est flexible. Cela fonctionnera correctement pour les nombres.
Seth Holladay
20
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);

[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);
sergeyz
la source
Pouvez-vous expliquer pourquoi vous n'avez pas simplement ajouté pushl'élément au tableau au lieu de l'utiliser concat? J'ai essayé d'utiliser push et cela a échoué. Je cherche une explication.
makenova
1
La raison réside dans la valeur de retour. concat retourne le tableau modifié (exactement ce qui doit être retourné dans la fonction de réduction), tandis que push retourne un index auquel vous pouvez accéder à la valeur poussée. Est-ce que ça répond à votre question?
sergeyz
Apparemment, cette solution est assez rapide en chrome (et en noeud). jsperf.com/reduce-for-distinct C'est la version ES6: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens
note complémentaire: cette implémentation n'est pas NaNconviviale
Alireza
19

Nous pouvons le faire en utilisant des ensembles ES6:

var duplicatedArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = Array.from(new Set(duplicatedArray));

console.log(uniqueArray);

// La sortie sera

uniqueArray = [1,2,3,4,5];
chinmayan
la source
17

Ce prototype getUniquen'est pas totalement correct, car si j'ai un tableau comme: ["1",1,2,3,4,1,"foo"]il retournera ["1","2","3","4"]et "1"est une chaîne et 1est un entier; ils sont différents.

Voici une bonne solution:

Array.prototype.unique = function(a){
    return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });

en utilisant:

var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();

Ce qui précède produira ["1",2,3,4,1,"foo"].

Gabriel Silveira
la source
1
Notez que $foo = 'bar'c'est la façon PHP de déclarer des variables. Cela fonctionnera en javascript, mais créera un global implicite, et ne devrait généralement pas être fait.
Camilo Martin
@CamiloMartin désolé mais vous vous trompez, $ foo est global car l'exemple n'est pas dans une fermeture et il lui manque le mot-clé var. Rien à voir avec le dollar jsfiddle.net/robaldred/L2MRb
Rob
8
@Rob c'est exactement ce que je dis, les gens de PHP penseront que $fooc'est la façon de déclarer des variables en javascript alors qu'elle l' var fooest réellement .
Camilo Martin
13

Sans étendre Array.prototype (on dit que c'est une mauvaise pratique) ou utiliser jquery / underscore, vous pouvez simplement filterle tableau.

En gardant la dernière occurrence:

    function arrayLastUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps last occurrence
            return c.indexOf(a, b + 1) < 0;
        });
    },

ou première occurrence:

    function arrayFirstUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps first occurrence
            return c.indexOf(a) === b;
        });
    },

Eh bien, c'est seulement javascript ECMAScript 5+, ce qui signifie seulement IE9 +, mais c'est bien pour un développement en HTML / JS natif (Windows Store App, Firefox OS, Sencha, Phonegap, Titanium, ...).

Cœur
la source
2
Le fait que ce soit js 1.6 ne signifie pas que vous ne pouvez pas l'utiliser filter. Sur la page MDN, ils ont une implémentation pour Internet Explorer, je veux dire, des navigateurs plus anciens. Aussi: JS 1.6 se réfère uniquement au moteur js de Firefox, mais la bonne chose à dire c'est qu'il s'agit d'ECMAScript 5.
Camilo Martin
@CamiloMartin J'ai changé 1.6 pour ECMAScript5. Merci.
Cœur
13

la magie

a.filter(e=>!(t[e]=e in t)) 

O (n) performance ; nous supposons que votre tableau est dans aet t={}. Explication ici (+ Impr . Jeppe )

Kamil Kiełczewski
la source
14
ce look tellement super cool, que sans une explication solide je suis tombé tu vas miner des bitcoins quand je l'exécute
Ondřej Želazko
3
ce que je voulais dire, c'est que vous devriez développer votre réponse avec quelques explications et commenté sa déconstruction. ne vous attendez pas à ce que les gens trouvent des réponses utiles comme celle-ci. (même si ça a l'air vraiment cool et ça marche probablement)
Ondřej Želazko
1
@Jeppe lorsque je lance votre amélioration, je ressens un effet aha (avant que je ne sache pas que je peux utiliser un inopérateur en dehors de l'autre construction que la forboucle: P) - Merci - je l'apprécie et je donnerai +2 à vos autres bonnes réponses .
Kamil Kiełczewski
2
cela ne crée-t-il pas une variable globale tqui reste vivante après le filtrage… ??
philipp
1
C'est vraiment une solution délicate. Malheureusement, vous devez déclarer t en dehors de l'expression, sauf si vous souhaitez définir une variable globale et / ou vous fier à votre module bundler pour masquer le global. Cela a l'air fantaisiste, mais faites-vous plaisir (et à votre équipe de développeurs) et écrivez simplement deux lignes, puis: let t = {}; a.filter (e =>! (t [e] = e in t))
ford04
13
[...new Set(duplicates)]

C'est le plus simple et référencé à partir de MDN Web Docs .

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]
ifelse.codes
la source
Bien que ce code puisse résoudre la question, y compris une explication de comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre message, et entraînerait probablement plus de votes positifs. N'oubliez pas que vous répondrez à la question des lecteurs à l'avenir, pas seulement à la personne qui pose la question maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limitations et hypothèses applicables.
id.ot
11

Si vous utilisez le framework Prototype, il n'est pas nécessaire de faire des boucles 'for', vous pouvez utiliser http://www.prototypejs.org/api/array/uniq comme ceci:

var a = Array.uniq();  

Ce qui produira un tableau en double sans doublons. Je suis tombé sur votre question en cherchant une méthode pour compter les enregistrements de tableaux distincts donc après

uniq ()

j'ai utilisé

Taille()

et il y avait mon résultat simple. ps Désolé si j'ai mal écrit quelque chose

modifier: si vous voulez échapper aux enregistrements non définis, vous pouvez ajouter

compact()

avant, comme ça:

var a = Array.compact().uniq();  
Decebal
la source
14
parce que j'ai trouvé une meilleure réponse, je pense que les sujets sont pour toutes les personnes et pas seulement pour celui qui a demandé
Decebal
11

Maintenant, en utilisant des ensembles, vous pouvez supprimer les doublons et les reconvertir en tableau.

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

console.log([...new Set(names)])

Une autre solution consiste à utiliser le tri et le filtrage

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);

Krishnadas PC
la source
10

C'est parce que 0c'est une valeur fausse en JavaScript.

this[i] sera falsifié si la valeur du tableau est 0 ou toute autre valeur falsifiée.

Luca Matteis
la source
Ahhhh, ok je vois maintenant ... mais y aurait-il une solution facile pour le faire fonctionner?
Mottie
10
Array.prototype.getUnique = function() {
    var o = {}, a = []
    for (var i = 0; i < this.length; i++) o[this[i]] = 1
    for (var e in o) a.push(e)
    return a
}
éphémère
la source
Je pense que cela ne fonctionnera pas si le tableau contient des objets / tableaux, et je ne suis pas sûr qu'il préservera le type de scalaires.
Camilo Martin
Oui, tout est stratifié. Cela pourrait être corrigé en stockant la valeur d'origine dans oau lieu d'un simple 1, bien que la comparaison d'égalité soit toujours dans le sens des chaînes (bien que, parmi toutes les égalités Javascript possibles, cela ne semble pas trop déraisonnable).
éphémère
Le Array.prototype ne pouvait être étendu qu'avec des méthodes non énumérables .... Object.defineProperty (Array.prototype, "getUnique", {}) ... mais l'idée d'utiliser un objet d'assistance est très agréable
bortunac
10

J'ai eu un problème légèrement différent où j'avais besoin de supprimer des objets avec des propriétés d'ID en double d'un tableau. cela a fonctionné.

let objArr = [{
  id: '123'
}, {
  id: '123'
}, {
  id: '456'
}];

objArr = objArr.reduce((acc, cur) => [
  ...acc.filter((obj) => obj.id !== cur.id), cur
], []);

console.log(objArr);

shunryu111
la source
9

La réponse la plus simple est:

const array = [1, 1, 2, 2, 3, 5, 5, 2];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]
Sukanta Bala
la source
Simple mais cela ne fonctionne pas avec un tableau d'objets.
Thanwa Ch.
7

Je ne sais pas pourquoi Gabriel Silveira a écrit la fonction de cette façon, mais une forme plus simple qui fonctionne aussi bien pour moi et sans la minification est:

Array.prototype.unique = function() {
  return this.filter(function(value, index, array) {
    return array.indexOf(value, index + 1) < 0;
  });
};

ou en CoffeeScript:

Array.prototype.unique = ->
  this.filter( (value, index, array) ->
    array.indexOf(value, index + 1) < 0
  )
Dan Fox
la source
7

Si vous êtes d'accord avec des dépendances supplémentaires, ou si vous avez déjà l'une des bibliothèques dans votre base de code, vous pouvez supprimer les doublons d'un tableau en place à l'aide de LoDash (ou Underscore).

Usage

Si vous ne l'avez pas déjà dans votre base de code, installez-le à l'aide de npm:

npm install lodash

Ensuite, utilisez-le comme suit:

import _ from 'lodash';
let idArray = _.uniq ([
    1,
    2,
    3,
    3,
    3
]);
console.dir(idArray);

En dehors:

[ 1, 2, 3 ]
NikeshPathania
la source
7

On y a beaucoup répondu, mais cela n'a pas répondu à mon besoin particulier.

De nombreuses réponses sont comme ceci:

a.filter((item, pos, self) => self.indexOf(item) === pos);

Mais cela ne fonctionne pas pour les tableaux d'objets complexes.

Disons que nous avons un tableau comme celui-ci:

const a = [
 { age: 4, name: 'fluffy' },
 { age: 5, name: 'spot' },
 { age: 2, name: 'fluffy' },
 { age: 3, name: 'toby' },
];

Si nous voulons que les objets avec des noms uniques, nous devons utiliser à la array.prototype.findIndexplace de array.prototype.indexOf:

a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
Dave
la source
Excellente solution, méfiez-vous qu'un nouveau tableau reviendra d'une fonction. (il ne se modifie pas)
Thanwa Ch.
6

De Shamasis Bhattacharya blog de (O (2n) la complexité du temps):

Array.prototype.unique = function() {
    var o = {}, i, l = this.length, r = [];
    for(i=0; i<l;i+=1) o[this[i]] = this[i];
    for(i in o) r.push(o[i]);
    return r;
};

Du blog de Paul Irish : amélioration sur JQuery .unique():

(function($){

    var _old = $.unique;

    $.unique = function(arr){

        // do the default behavior only if we got an array of elements
        if (!!arr[0].nodeType){
            return _old.apply(this,arguments);
        } else {
            // reduce the array to contain no dupes via grep/inArray
            return $.grep(arr,function(v,k){
                return $.inArray(v,arr) === k;
            });
        }
    };
})(jQuery);

// in use..
var arr = ['first',7,true,2,7,true,'last','last'];
$.unique(arr); // ["first", 7, true, 2, "last"]

var arr = [1,2,3,4,5,4,3,2,1];
$.unique(arr); // [1, 2, 3, 4, 5]
Frosty Z
la source
6

Trouver des valeurs de tableau uniques dans une méthode simple

function arrUnique(a){
  var t = [];
  for(var x = 0; x < a.length; x++){
    if(t.indexOf(a[x]) == -1)t.push(a[x]);
  }
  return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]
Saravanan Rajaraman
la source
6

Il semble que nous ayons perdu la réponse de Rafael , qui a été la réponse acceptée pendant quelques années. C'était (au moins en 2017) la solution la plus performante si vous n'avez pas de tableau de type mixte :

Array.prototype.getUnique = function(){
    var u = {}, a = [];
    for (var i = 0, l = this.length; i < l; ++i) {
        if (u.hasOwnProperty(this[i])) {
            continue;
        }
        a.push(this[i]);
        u[this[i]] = 1;
    }
return a;
}

Si vous n'avez un tableau de type mixte, vous pouvez sérialiser la clé de hachage:

Array.prototype.getUnique = function() {
    var hash = {}, result = [], key; 
    for ( var i = 0, l = this.length; i < l; ++i ) {
        key = JSON.stringify(this[i]);
        if ( !hash.hasOwnProperty(key) ) {
            hash[key] = true;
            result.push(this[i]);
        }
    }
    return result;
}
Jason
la source
5

Pour résoudre le problème dans l'autre sens, il peut être utile de ne pas avoir de doublon pendant le chargement de votre tableau, comme le ferait l'objet Set, mais il n'est pas encore disponible dans tous les navigateurs. Il économise de la mémoire et est plus efficace si vous avez besoin de regarder son contenu plusieurs fois.

Array.prototype.add = function (elem) {
   if (this.indexOf(elem) == -1) {
      this.push(elem);
   }
}

Échantillon:

set = [];
[1,3,4,1,2,1,3,3,4,1].forEach(function(x) { set.add(x); });

Vous donne set = [1,3,4,2]

Le Droid
la source