Comment obtenir des valeurs uniques dans un tableau

168

Comment puis-je obtenir une liste de valeurs uniques dans un tableau? Dois-je toujours utiliser un deuxième tableau ou y a-t-il quelque chose de similaire au hashmap de java en JavaScript?

Je vais utiliser uniquement JavaScript et jQuery . Aucune bibliothèque supplémentaire ne peut être utilisée.

Astronaute
la source
2
stackoverflow.com/questions/5381621/… - décrit exactement ce que vous voulez que je pense?
SpaceBison
1
êtes-vous ouvert à l'utilisation de la underscore.jsbibliothèque?
jakee
un hashmap java est fondamentalement identique à un objet javascript. la syntaxe est {"key": "value", "key2": "value2"}
Ian
Les API de collecte JavaScript / TypeScript sont terribles par rapport à Scala,list.toSet
Jordan Stewart

Réponses:

120

Depuis que j'en ai parlé dans les commentaires de la réponse de @ Rocket, je peux aussi bien fournir un exemple qui n'utilise aucune bibliothèque. Cela nécessite deux nouvelles fonctions de prototype, containsetunique

Array.prototype.contains = function(v) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] === v) return true;
  }
  return false;
};

Array.prototype.unique = function() {
  var arr = [];
  for (var i = 0; i < this.length; i++) {
    if (!arr.contains(this[i])) {
      arr.push(this[i]);
    }
  }
  return arr;
}

var duplicates = [1, 3, 4, 2, 1, 2, 3, 8];
var uniques = duplicates.unique(); // result = [1,3,4,2,8]

console.log(uniques);

Pour plus de fiabilité, vous pouvez remplacer containspar le indexOfshim de MDN et vérifier si chaque élément indexOfest égal à -1: documentation

jackwanders
la source
1
~a.indexOf(b) === (a.indexOf(b) == -1)
Orwellophile
1
@Lexynux: c'était le genre de réponse geek javascript-nerd qui ne devrait être utilisée que par quelqu'un qui comprend ce que cela signifie, et peut-être même pas alors. Ce qu'il dit, c'est que l'écriture if (~a.indexOf(b)) ...est identique à l'écriture plus longue if (a.indexOf(b) == -1) ....
Orwellophile
4
cela a une complexité d'exécution élevée (pire des cas: O (n ^ 2))
Rahul Arora
1
c'est une mise en œuvre vraiment inefficace. vérifier le tableau de résultats pour voir s'il contient déjà un élément est horrible. une meilleure approche serait d'utiliser un objet qui suit les décomptes, ou si vous ne voulez pas utiliser le stockage auxiliaire, triez-le d'abord en O (n log n) puis en un balayage linéaire et comparez les éléments côte à côte
Isaiah Lee
1
Avons-nous vraiment besoin de la fonction "contient"?
Animesh Kumar
206

Ou pour ceux qui recherchent un one-liner (simple et fonctionnel), compatible avec les navigateurs actuels :

let a = ["1", "1", "2", "3", "3", "1"];
let unique = a.filter((item, i, ar) => ar.indexOf(item) === i);
console.log(unique);

Mise à jour 18-04-2017

Il semble que 'Array.prototype.includes' soit désormais largement pris en charge dans les dernières versions des navigateurs principaux ( compatibilité )

Mise à jour 29-07-2015:

Il y a des plans en cours pour que les navigateurs prennent en charge une méthode standardisée «Array.prototype.includes», qui bien qu'elle ne répond pas directement à cette question; est souvent liée.

Usage:

["1", "1", "2", "3", "3", "1"].includes("2");     // true

Pollyfill ( support du navigateur , source de mozilla ):

// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(searchElement, fromIndex) {

      // 1. Let O be ? ToObject(this value).
      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If len is 0, return false.
      if (len === 0) {
        return false;
      }

      // 4. Let n be ? ToInteger(fromIndex).
      //    (If fromIndex is undefined, this step produces the value 0.)
      var n = fromIndex | 0;

      // 5. If n ≥ 0, then
      //  a. Let k be n.
      // 6. Else n < 0,
      //  a. Let k be len + n.
      //  b. If k < 0, let k be 0.
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

      // 7. Repeat, while k < len
      while (k < len) {
        // a. Let elementK be the result of ? Get(O, ! ToString(k)).
        // b. If SameValueZero(searchElement, elementK) is true, return true.
        // c. Increase k by 1.
        // NOTE: === provides the correct "SameValueZero" comparison needed here.
        if (o[k] === searchElement) {
          return true;
        }
        k++;
      }

      // 8. Return false
      return false;
    }
  });
}
Josh Mc
la source
C'est presque du copier-coller de kennebec, mais il est vrai que passer le tableau en tant que paramètre plutôt que d'utiliser la fermeture améliorera probablement les performances.
- je dois dire que je n'ai pas connecté les points, simplement scanné à travers pour une seule ligne, ressemblait à un grand message alors ignoré, je suis allé trouver une source alternative et re-publié pour que d'autres le trouvent rapidement. Cela dit, votre droit; à peu près la même chose que kennebec.
Josh Mc
Nice - Je n'ai pas réalisé que le filtre était envoyé dans le tableau en tant que paramètre et je ne voulais pas travailler sur un objet externe. C'est exactement ce dont j'ai besoin - ma version de javascript (ancienne version de xerces) n'aura pas les nouveaux bonus pendant un certain temps.
Gerard ONeill
1
@GerardONeill ouais, dans certaines situations, c'est très vital, par exemple s'il est fonctionnellement chaîné et que vous voulez accéder à un tableau qui n'a pas reçu de variable telle que .map (...). Filter (...)
Josh Mc
@Josh Mc, pourriez-vous d'une manière ou d'une autre utiliser «comprend» dans la méthode «unique»?
vkelman
144

Voici une solution beaucoup plus propre pour ES6 que je vois n'est pas incluse ici. Il utilise le Set et l' opérateur de diffusion :...

var a = [1, 1, 2];

[... new Set(a)]

Quels retours [1, 2]

Charles Clayton
la source
1
C'est si intelligent!
Josh Mc
1
Maintenant, C'est un one-liner!
Mac
11
Dans Typescript, vous devez utiliser Array.from(... new Set(a))car Set ne peut pas être implicitement converti en type tableau. Juste un avertissement!
Zachscs
4
@Zachscs, j'ai eu une erreur de compilation lorsque j'ai essayé cela. Voulez-vous dire juste Array.from(new Set(a))? Cela semble fonctionner.
adam0101
72

Une doublure, pur 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
4
Je ne sais pas pourquoi cela a été rejeté. Il peut être peu obscur au début, et peut - être classé comme «intelligent» et pas pragmatique à lire, mais c'est déclaratif, non destructif et concis, là où la plupart des autres réponses font défaut.
Larry
1
@Larry, cela a été voté à la baisse car exactement la même réponse a été fournie des années avant celle-ci.
Alex Okrushko
@AlexOkrushko assez juste - a raté cette réponse à cause de la façon dont elle a été formatée
Larry
7
Tout est une ligne si vous mettez tout sur une seule ligne :-)
Gary McGill
Cela pourrait être bien de resserrer la a.indexOf(x) === inote d' égalité des trois signes d'égalité.
treejanitor
21

En utilisant EcmaScript 2016, vous pouvez simplement le faire comme ceci.

 var arr = ["a", "a", "b"];
 var uniqueArray = Array.from(new Set(arr)); // Unique Array ['a', 'b'];

Les ensembles sont toujours uniques et à l'aide de, Array.from()vous pouvez convertir un ensemble en tableau. Pour référence, jetez un œil aux documentations.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects /Ensemble

Adeel Imran
la source
1
C'est la réponse que vous devriez utiliser. indexOf()les réponses sont terribles parce qu'elles sont O (N ^ 2). Les réponses étalées sont correctes mais ne fonctionneront pas pour les grands tableaux. C'est la meilleure approche.
Timmmm
20

Maintenant dans ES6, nous pouvons utiliser la fonction ES6 nouvellement introduite

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

OU par Array répartit la syntaxe sur les itérables

let items = [1,1,1,1,3,4,5,2,23,1,4,4,4,2,2,2]; 
let uniqueItems = [...new Set(items)];

Il renverra le résultat unique.

[1, 3, 4, 5, 2, 23]
Rohit.007
la source
1
La solution la plus propre!
Dimitris Filippou
C'est la voie à suivre, dans un environnement JS qui prend en charge new Setcomme celui-ci (tel que Angular / TypeScript moderne)
Don Cheadle
1
Il est intéressant de noter que vous pouvez également utiliser la syntaxe de diffusion Array sur des itérables comme Set et Map: let items = [1,1,1,1,3,4,5,2,23,1,4,4,4,2,2,2]; let uniqueItems = [...new Set(items)];
jacobedawson
J'adore ces réponses ES6!
Glen Thompson
1
C'est identique à la réponse de @Adeel Imran. Veuillez ne pas donner de réponses qui sont exactement les mêmes que les réponses existantes.
Timmmm
16

Si vous souhaitez conserver la matrice d'origine intacte,

vous avez besoin d'un second tableau pour contenir les éléments uniqe du premier

La plupart des navigateurs ont Array.prototype.filter:

var unique= array1.filter(function(itm, i){
    return array1.indexOf(itm)== i; 
    // returns true for only the first instance of itm
});


//if you need a 'shim':
Array.prototype.filter= Array.prototype.filter || function(fun, scope){
    var T= this, A= [], i= 0, itm, L= T.length;
    if(typeof fun== 'function'){
        while(i<L){
            if(i in T){
                itm= T[i];
                if(fun.call(scope, itm, i, T)) A[A.length]= itm;
            }
            ++i;
        }
    }
    return A;
}
 Array.prototype.indexOf= Array.prototype.indexOf || function(what, i){
        if(!i || typeof i!= 'number') i= 0;
        var L= this.length;
        while(i<L){
            if(this[i]=== what) return i;
            ++i;
        }
        return -1;
    }
Kennebec
la source
14

De nos jours, vous pouvez utiliser le type de données Set d'ES6 pour convertir votre baie en un ensemble unique. Ensuite, si vous devez utiliser des méthodes de tableau, vous pouvez le reconvertir en un tableau:

var arr = ["a", "a", "b"];
var uniqueSet = new Set(arr); // {"a", "b"}
var uniqueArr = Array.from(uniqueSet); // ["a", "b"]
//Then continue to use array methods:
uniqueArr.join(", "); // "a, b"
Fawntasia
la source
1
Chouette, ce serait bien de voir quelques chiffres de performances, je pense que la conversion de ces ensembles en particulier s'ils sont très dynamiques et grands pourrait être un problème de performance, le seul moyen de le savoir est de tester :)
Astronaut
2
Si vous utilisez un transpilateur ou si vous êtes dans un environnement qui le prend en charge, vous pouvez faire la même chose de manière plus concise que:var uniqueArr = [...new Set(arr)]; // ["a", "b"]
Stenerson
Hey @Astronaut a fait des tests sur les performances comme tu l'as dit?
alexventuraio
8

Pas natif en Javascript, mais de nombreuses bibliothèques ont cette méthode.

Underscore.js _.uniq(array)( lien ) fonctionne assez bien ( source ).

Calvin
la source
Merci pour le partage! Cette fonction prend l'itérateur et le contexte avec un tableau comme une liste d'arguments de la v1.4.3.
Kunj
6

En utilisant jQuery, voici une fonction unique de Array que j'ai créée:

Array.prototype.unique = function () {
    var arr = this;
    return $.grep(arr, function (v, i) {
        return $.inArray(v, arr) === i;
    });
}

console.log([1,2,3,1,2,3].unique()); // [1,2,3]
Fusée Hazmat
la source
5
si vous allez utiliser jQuery dans le prototype d'un objet javascript principal, ne serait-il pas préférable d'écrire une fonction jQuery, telle que $.uniqueArray(arr)? L'intégration de références à jQuery dans Arrayle prototype de 's semble discutable
jackwanders
1
@jackwanders: Qu'est-ce qui est si discutable à ce sujet? Si vous avez jQuery sur la page, utilisons-le.
Rocket Hazmat
Juste que la nouvelle fonction unique que vous avez écrite dépend maintenant de jQuery; vous ne pouvez pas le déplacer vers un nouveau site ou une nouvelle application sans vous assurer que jQuery y est utilisé.
jackwanders
2
c'était mon point; si vous allez utiliser jQuery, intégrez la fonction elle-même à jQuery. Si j'allais étendre le prototype d'un objet core, je m'en tiendrais au core javascript, juste pour garder les choses réutilisables. Si quelqu'un d'autre regarde votre code, il est évident qu'il $.uniqueArraydépend de jQuery; moins évident qui l' Array.prototype.uniqueest aussi.
jackwanders
1
@jackwanders: Je suppose. J'utilise ceci dans mon code, car j'utilise toujours jQuery, et j'aime juste étendre prototypes. Mais, je comprends votre point maintenant. Je vais laisser ça ici de toute façon.
Rocket Hazmat
6

Solution courte et douce utilisant un deuxième tableau;

var axes2=[1,4,5,2,3,1,2,3,4,5,1,3,4];

    var distinct_axes2=[];

    for(var i=0;i<axes2.length;i++)
        {
        var str=axes2[i];
        if(distinct_axes2.indexOf(str)==-1)
            {
            distinct_axes2.push(str);
            }
        }
    console.log("distinct_axes2 : "+distinct_axes2); // distinct_axes2 : 1,4,5,2,3
Pradip Shenolkar
la source
Court? et doux? Avez-vous examiné les meilleures solutions?
Alex Okrushko
5

Rapide, compact, sans boucles imbriquées, fonctionne avec n'importe quel objet, pas seulement des chaînes et des nombres, prend un prédicat et seulement 5 lignes de code !!

function findUnique(arr, predicate) {
  var found = {};
  arr.forEach(d => {
    found[predicate(d)] = d;
  });
  return Object.keys(found).map(key => found[key]); 
}

Exemple: pour rechercher des éléments uniques par type:

var things = [
  { name: 'charm', type: 'quark'},
  { name: 'strange', type: 'quark'},
  { name: 'proton', type: 'boson'},
];

var result = findUnique(things, d => d.type);
//  [
//    { name: 'charm', type: 'quark'},
//    { name: 'proton', type: 'boson'}
//  ] 

Si vous voulez qu'il trouve le premier élément unique au lieu du dernier, ajoutez un enregistrement found.hasOwnPropery ().

user1618323
la source
4

Vous n'avez besoin que de vanilla JS pour trouver des uniques avec Array.some et Array.reduce. Avec la syntaxe ES2015, il n'y a que 62 caractères.

a.reduce((c, v) => b.some(w => w === v) ? c : c.concat(v)), b)

Array.some et Array.reduce sont pris en charge dans IE9 + et d'autres navigateurs. Modifiez simplement les fonctions de la grosse flèche pour que les fonctions normales prennent en charge dans les navigateurs qui ne prennent pas en charge la syntaxe ES2015.

var a = [1,2,3];
var b = [4,5,6];
// .reduce can return a subset or superset
var uniques = a.reduce(function(c, v){
    // .some stops on the first time the function returns true                
    return (b.some(function(w){ return w === v; }) ?  
      // if there's a match, return the array "c"
      c :     
      // if there's no match, then add to the end and return the entire array                                        
      c.concat(v)}),                                  
  // the second param in .reduce is the starting variable. This is will be "c" the first time it runs.
  b);                                                 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects / Array / Réduire

chiot
la source
4

La majorité des solutions ci-dessus ont une complexité d'exécution élevée.

Voici la solution qui utilise reduceet peut faire le travail en un temps O (n).

Array.prototype.unique = Array.prototype.unique || function() {
        var arr = [];
	this.reduce(function (hash, num) {
		if(typeof hash[num] === 'undefined') {
			hash[num] = 1; 
			arr.push(num);
		}
		return hash;
	}, {});
	return arr;
}
    
var myArr = [3,1,2,3,3,3];
console.log(myArr.unique()); //[3,1,2];

Remarque:

Cette solution ne dépend pas de réduire. L'idée est de créer une carte d'objets et d'en pousser des uniques dans le tableau.

Rahul Arora
la source
1

Manière ES6:

const uniq = (arr) => (arr.filter((item, index, arry) => (arry.indexOf(item) === index)));
Ashwin Aggarwal
la source
1

vous pouvez utiliser,

let arr1 = [1,2,1,3];
let arr2 = [2,3,4,5,1,2,3];

let arr3 = [...new Set([...arr1,...arr2])];

cela vous donnera des éléments uniques,

**> mais il y a un hic,

pour ce "1" et 1 et sont des éléments diff, **

la deuxième option consiste à utiliser la méthode de filtrage sur le tableau.

niranjan harpale
la source
1

Vous pouvez entrer un tableau avec des doublons et la méthode ci-dessous retournera un tableau avec des éléments uniques.

function getUniqueArray(array){
    var uniqueArray = [];
    if (array.length > 0) {
       uniqueArray[0] = array[0];
    }
    for(var i = 0; i < array.length; i++){
        var isExist = false;
        for(var j = 0; j < uniqueArray.length; j++){
            if(array[i] == uniqueArray[j]){
                isExist = true;
                break;
            }
            else{
                isExist = false;
            }
        }
        if(isExist == false){
            uniqueArray[uniqueArray.length] = array[i];
        }
    }
    return uniqueArray;
}
Muhammad Haroon Iqbal
la source
0

Le seul problème avec les solutions proposées jusqu'à présent est l'efficacité. Si cela vous préoccupe (et vous devriez probablement), vous devez éviter les boucles imbriquées: pour * for, filter * indexOf, grep * inArray, elles itèrent toutes le tableau plusieurs fois. Vous pouvez implémenter une seule boucle avec des solutions comme celle-ci ou celle-ci

Jesús Carrera
la source
0
Array.prototype.unique = function () {
    var dictionary = {};
    var uniqueValues = [];
    for (var i = 0; i < this.length; i++) {
        if (dictionary[this[i]] == undefined){
            dictionary[this[i]] = i;
            uniqueValues.push(this[i]);
        }
    }
    return uniqueValues; 
}
Hexer338
la source
0

J'ai essayé ce problème en pur JS. J'ai suivi les étapes suivantes 1. Trier le tableau donné, 2. parcourir le tableau trié, 3. Vérifier la valeur précédente et la valeur suivante avec la valeur actuelle

// JS
var inpArr = [1, 5, 5, 4, 3, 3, 2, 2, 2,2, 100, 100, -1];

//sort the given array
inpArr.sort(function(a, b){
    return a-b;
});

var finalArr = [];
//loop through the inpArr
for(var i=0; i<inpArr.length; i++){
    //check previous and next value 
  if(inpArr[i-1]!=inpArr[i] && inpArr[i] != inpArr[i+1]){
        finalArr.push(inpArr[i]);
  }
}
console.log(finalArr);

Démo

Sanketh Nayak
la source
0
function findUniques(arr){
  let uniques = []
  arr.forEach(n => {
    if(!uniques.includes(n)){
      uniques.push(n)
    }       
  })
  return uniques
}

let arr = ["3", "3", "4", "4", "4", "5", "7", "9", "b", "d", "e", "f", "h", "q", "r", "t", "t"]

findUniques(arr)
// ["3", "4", "5", "7", "9", "b", "d", "e", "f", "h", "q", "r", "t"]
smithWEBtek
la source
0

Ayant à l'esprit que indexOfretournera la première occurrence d'un élément, vous pouvez faire quelque chose comme ceci:

Array.prototype.unique = function(){
        var self = this;
        return this.filter(function(elem, index){
            return self.indexOf(elem) === index;
        })
    }
Nikksan
la source
0

Une autre pensée de cette question. Voici ce que j'ai fait pour y parvenir avec moins de code.

var distinctMap = {};
var testArray = ['John', 'John', 'Jason', 'Jason'];
for (var i = 0; i < testArray.length; i++) {
  var value = testArray[i];
  distinctMap[value] = '';
};
var unique_values = Object.keys(distinctMap);

console.log(unique_values);

Xing-Wei Lin
la source
0

function findUnique(arr) {
  var result = [];
  arr.forEach(function(d) {
    if (result.indexOf(d) === -1)
      result.push(d);
  });
  return result;
}

var unique = findUnique([1, 2, 3, 1, 2, 1, 4]); // [1,2,3,4]
console.log(unique);

user1739150
la source
0

Voici une approche avec une equalsfonction personnalisable qui peut être utilisée pour les primitives ainsi que pour les objets personnalisés:

Array.prototype.pushUnique = function(element, equalsPredicate = (l, r) => l == r) {
    let res = !this.find(item => equalsPredicate(item, element))
    if(res){
        this.push(element)
    }
    return res
}

usage:

//with custom equals for objects
myArrayWithObjects.pushUnique(myObject, (left, right) => left.id == right.id)

//with default equals for primitives
myArrayWithPrimitives.pushUnique(somePrimitive)
virus
la source
0

Ma réponse utilise Array.filteret Array.indexOfméthodes pour obtenir les valeurs uniques

array.filter((value, index, self)=>self.indexOf(value)===index)

J'ai vu cette approche sur un site Web, mais leur code est différent de ce qu'il en a ici. J'ai simplifié le code en une seule ligne et je l'ai affiché ici pour que quelqu'un en profite

Remarque: mon approche est similaire ou identique à celle publiée par Josh. Je le laisse ici car les noms des variables sont explicites dans mon code.

Anand Raj
la source
-1

Je pensais juste si nous pouvions utiliser la recherche linéaire pour éliminer les doublons:

JavaScript:
function getUniqueRadios() {

var x=document.getElementById("QnA");
var ansArray = new Array();
var prev;


for (var i=0;i<x.length;i++)
  {
    // Check for unique radio button group
    if (x.elements[i].type == "radio")
    {
            // For the first element prev will be null, hence push it into array and set the prev var.
            if (prev == null)
            {
                prev = x.elements[i].name;
                ansArray.push(x.elements[i].name);
            } else {
                   // We will only push the next radio element if its not identical to previous.
                   if (prev != x.elements[i].name)
                   {
                       prev = x.elements[i].name;
                       ansArray.push(x.elements[i].name);
                   }
            }
    }

  }

   alert(ansArray);

}

HTML:

<body>

<form name="QnA" action="" method='post' ">

<input type="radio"  name="g1" value="ANSTYPE1"> good </input>
<input type="radio" name="g1" value="ANSTYPE2"> avg </input>

<input type="radio"  name="g2" value="ANSTYPE3"> Type1 </input>
<input type="radio" name="g2" value="ANSTYPE2"> Type2 </input>


<input type="submit" value='SUBMIT' onClick="javascript:getUniqueRadios()"></input>


</form>
</body>
Suresh
la source
-1

Voici la solution unique au problème:

var seriesValues = [120, 120, 120, 120];
seriesValues = seriesValues.filter((value, index, seriesValues) => (seriesValues.slice(0, index)).indexOf(value) === -1);
console.log(seriesValues);

Copiez-collez ceci dans la console du navigateur et obtenez les résultats, yo :-)

Manish Kumar
la source
-2

J'ai une fonction JQuery Unique intégrée.

uniqueValues= jQuery.unique( duplicateValues );

Pour plus d'informations, vous pouvez vous référer aux documentations de l'API jquery.

http://api.jquery.com/jquery.unique/

Mitul Maheshwari
la source
8
Notez que cela ne fonctionne que sur des tableaux d'éléments DOM, pas sur des chaînes ou des nombres. - citation de la documentation.
mco