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.


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


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

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]


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 +


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


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]

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;
    }

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"

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 ).


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]

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

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 ().


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


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.


1

Manière ES6:

const uniq = (arr) => (arr.filter((item, index, arry) => (arry.indexOf(item) === index)));

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.


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;
}

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


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; 
}

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


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"]

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


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);


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);


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)

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.


-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>

-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 :-)


-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/


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.