Supprimer les doublons d'un tableau


100

J'ai un tableau d'objets qui ressemble à ceci:

var array = [
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
    ...
];

Comme vous pouvez le voir, certains noms sont répétés. Je veux obtenir un nouveau tableau avec des noms uniquement, mais si un nom se répète, je ne veux pas l'ajouter à nouveau. Je veux ce tableau:

var newArray = ["Name1", "Name2"];

J'essaye de faire ça avec map:

var newArray = array.map((a) => {
    return a.name;
});

Mais le problème est que cela renvoie:

newArray = ["Name1", "Name1", "Name2", "Name2"];

Comment puis-je définir une condition à l'intérieur map, pour qu'elle ne renvoie pas un élément qui existe déjà? Je veux faire cela avec mapou avec une autre fonctionnalité ECMAScript 5 ou ECMAScript 6.


2
Supprimez ensuite les doublons du tableau.
Tushar


1
duplication possible de Remove Duplicates from JavaScript Array
Bergi

Pourquoi la ligne contenant id: 125 ne se termine-t-elle pas par une virgule?
Peter Mortensen

Veuillez noter que toutes les réponses utilisant .indexOf () auront des performances médiocres si le tableau est grand, en raison de leur complexité temporelle quadratique . Je recommanderais d'utiliser ES6 Set ou d'utiliser un objet ES5 comme dictionnaire.
joeytwiddle

Réponses:


210

Avec ES6, vous pouvez utiliser Setpour des valeurs uniques, après avoir mappé uniquement les noms des objets.

Cette proposition utilise une syntaxe étalée... pour collecter les éléments dans un nouveau tableau.

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      names = [...new Set(array.map(a => a.name))];

console.log(names);


9
Pour mon info .. que fait ...-on?
Rajshekar Reddy

9
@RajshekarReddy, c'est une syntaxe étalée... pour construire un tableau avec un objet itérable.
Nina Scholz

5
Je ne vote pas pour le "Set" lui-même, mais plutôt pour l'utilisation intelligente de l'opérateur de diffusion. C'est toujours agréable d'apprendre quelque chose de nouveau ou de voir quelque chose de nouveau appliqué à un exemple pratique, donc cet article mérite un vote favorable.
briosheje

33
C'est juste mon opinion personnelle, mais plutôt commune . L'utilisation Array.fromtransmet beaucoup mieux l'intention de conversion (et est également plus facile à comprendre / à rechercher pour les débutants), la syntaxe de diffusion ne doit être utilisée que lorsque l'élément diffusé est l'un des nombreux. Peut-être que l'appeler une «mauvaise pratique» est un peu trop dur, désolé, j'espère juste que cela ne deviendra pas un langage courant.
Bergi

3
@KRyan est-ce que ES6 est si nouveau? Il a été finalisé en juin 2015. Il est temps de commencer à comprendre et à utiliser les nouvelles fonctionnalités
edc65

64

Si vous recherchez une solution JavaScript qui n'est pas ES 6 (pas de Set), vous pouvez utiliser la méthode Arrayreduce :

var array=[
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];
var names = array.reduce(function (a, b) {
  if (a.indexOf(b.name) == -1) {
    a.push(b.name)
  }
  return a;
}, []);

console.log(names);


2
Cette réponse a également l'avantage de ne pas nécessiter de tableaux ou d'ensembles intermédiaires, en particulier ceux contenant uniquement les noms eux-mêmes. (Cela va directement du tableau d'objets au tableau d'objets.) +1
jpmc26

@Iwrestledabearonce, ce deuxième paramètre n'est-il pas le même objet que celui renvoyé?
Kaiido

Oui mais il ne passe pas directement de l'un à l'autre comme le tri ou le filtre, il est reconstruit à partir d'un tableau vide.
J'ai combattu un ours une fois.

1
@Dekel - Version plus petite :)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
Rayonne

3
@Rayon S'il vous plaît ne faites pas trop attention à la taille du code source, c'est un écueil courant pour de nombreux programmeurs autonomes, le plus important, votre programme doit être efficace et lisible.
feuille du

16

Personnellement, je ne vois pas pourquoi tout le monde a envie d'ES 6. Si c'était mon code, je préférerais prendre en charge autant de navigateurs que possible.

var array=[
{id:123, value:"value1", name:"Name1"},
{id:124, value:"value2", name:"Name1"},
{id:125, value:"value3", name:"Name2"},
{id:126, value:"value4", name:"Name2"}
];

   // Create array of unique names
var a = (function(a){
  for (var i = array.length; i--;)
    if (a.indexOf(array[i].name) < 0) a.push(array[i].name);
  return a;
})([]);

console.log(a);


2
Ne serait-il pas plus performant d'utiliser un objet comme dictionnaire au lieu de indexOf-ing tout le temps? Par exemple, faire added[name] = trueet if(added[name])au lieu de if(a.indexOf(name)).
Bojidar Marinov

7
"Personnellement, je ne vois pas pourquoi tout le monde a envie d'es6" pourquoi est-ce que vous courtisez un goto fail, faites-vous fuir votre variable d'index dans la portée englobante, etc. etc. ES6 a été codifié dans la norme pour de bonnes raisons, et c'est pour la plupart partie trivialement facile à polyfill / transformer pour les navigateurs plus anciens.
Jared Smith

5
Plus lisible? C'est une boucle freaking for qui pousse les éléments sur un tableau. Ça ne devient pas beaucoup plus lisible que ça ......
J'ai combattu un ours une fois.

4
Honnêtement, vous avez l'impression d'essayer trop fort de trouver quelque chose à redire.
J'ai combattu un ours une fois.

2
"Je fais référence au fameux bogue SSL d'Apple" - Ce n'est pas si célèbre que vous puissiez simplement y faire référence dans des commentaires aléatoires hors contexte et vous attendre à ce que les gens le reconnaissent.
Hejazzman

14

Vous pouvez aussi simplement combiner mapavecfilter

var array = [
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];

var unique = array
  .map( item => item.name )
  .filter( ( item, idx, arr ) => arr.indexOf( item ) == idx ) 

console.log(unique)


11

Vous pouvez utiliser Object.keys () pour obtenir le tableau des noms de propriétés énumérables propres à un objet donné à partir du résultat de l'objet de l'itération de la arrayvariable avec Array.prototype.reduce () où les clés sont les noms détruits

Code:

const array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}],
      names = Object.keys(array.reduce((a, { name }) => (a[name] = 1, a), {}))

console.log(names)


7

Beaucoup de bonnes réponses ici. Je voudrais juste contribuer avec une certaine diversité dans l'espoir de vous donner une autre perspective.

Les tableaux sont de type objet en JavaScript, ils peuvent donc être utilisés comme hachage en même temps. En utilisant cette fonctionnalité, nous pouvons grandement simplifier le travail à effectuer en une seule opération de réduction avec une complexité de temps O (n).

Si vous n'êtes pas satisfait du fait que votre tableau contienne des propriétés autres que les clés du tableau, vous pouvez également envisager de conserver un objet de hachage séparé.

var array = [{id:123, value:"value1", name:"Name1"},
             {id:124, value:"value2", name:"Name1"},
             {id:125, value:"value3", name:"Name2"},
             {id:126, value:"value4", name:"Name2"}
            ],
result = array.reduce((p,c) => p[c.name] ? p : (p[c.name] = true, p.push(c.name), p), []);
console.log(result);


Cette solution ES5 a de bonnes performances, mais l'utilisation de la baie à deux fins est déroutante et dangereuse si l'un des noms n'est qu'un nombre. Je recommanderais de faire le p[c.name]truc sur un objet séparé o, puis de le jeter par la suite.
joeytwiddle

7

Je suis d'accord que si vous n'avez besoin que des namevaleurs, Setc'est la voie à suivre.

Cependant , si vous souhaitez obtenir un tableau d'objets uniques en fonction de la namepropriété, je vous suggère d'utiliser un fichier Map. Un moyen rapide de créer une carte consiste à utiliser un tableau de [key, value]tableaux:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      unique = new Map(array.map(obj => [obj.name, obj]));

// To get the unique objects
const uniques = Array.from(unique.values());

// Get the names like you already did:
console.log("Names:", uniques.map(obj => obj.name));

// If you ever need the complete array of unique objects, you got a ref:
console.log(JSON.stringify(uniques));
.as-console-wrapper { min-height: 100%; }

Un avantage supplémentaire de Mapest que vous obtenez à la fois la filterfonctionnalité qui élimine les non-uniques, sans perdre la connexion avec les objets source. Bien sûr, cela n'est nécessaire que si vous avez besoin de référencer plusieurs fois l'ensemble unique d'objets.



2

Avec ES6, cela devrait faire l'affaire.

var array=[
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
];

var set = new Set();

array.forEach((a)=>{
    set.add(a.name);
}); 

console.log(Array.from(set));


11
mapest censé être utilisé avec des fonctions pures, sans effets secondaires. Ce serait un travail pour forEachou reduce.
Vincent van der Weele

1

En utilisant UnderscoreJS,

array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}];
get_names =  _.pluck(_.uniq(array, 'name'), 'name')
console.log(get_names)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

»


0

Dans ES5, utilisez un objet comme dictionnaire pour les performances O ( n ).

Cela ne fonctionnera que si toutes les clés sont des chaînes.

var array = [
    {id: 123, value: "value1", name: "Name1"},
    {id: 124, value: "value2", name: "Name1"},
    {id: 125, value: "value3", name: "Name2"},
    {id: 126, value: "value4", name: "Name2"}
];

var allNames = array.map(item => item.name);

var map = {};
allNames.forEach(name => {
  map[name] = true;
});
var uniqueNames = Object.keys(map);

console.log(uniqueNames);

Vous pouvez faire la même chose dans une expression si vous le souhaitez:

var uniqueNames = Object.keys(allNames.reduce((m, n) => (m[n] = true, m), {}));

mais je trouve la forme impérative plus facile à lire.


J'ai utilisé les fonctions fléchées ES6 pour plus de clarté. Si vous ciblez vraiment ES5 sans transpilateur, vous devrez utiliser le formulaire complet à la place:function (item) { return item.name; }
joeytwiddle

Au lieu de celaObject.keys() , cette réponse utilise filter()pour ne conserver que les noms qui n'ont pas encore été stockés dans la carte, ce qui est assez élégant.
joeytwiddle


0

Utilisation array#forEach() et des array#indexOf()méthodes comme celle-ci si vous voulez encore une compatibilité maximale , une syntaxe concise:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }]

// initialize an empty array named names
let names = [];

// iterate through every element of `array` & check if it's 'name' key's value already in names array if not ADD it 
array.forEach(function(element) { if (names.indexOf(element.name) === -1) names.push(element.name) });
// or use tilde like this:
//array.forEach(function(element) { if (~names.indexOf(element.name)) names.push(element.name) });

console.log(names);

Cependant, si la compatibilité est pas un problème utilisation ECMAScript 6 de l » Setobjet,array#map et des Array.from()méthodes comme celle - ci:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }];

// iterate through every element from array using map and store it in Set(a Set won't have duplicates) then convert the Set back to Array(using Array.from)
let names = Array.from(new Set(array.map(element => element.name)));

console.log(names);


0

C'est comme ça que je l'ai fait, en utilisant un tableau vide séparé.

var array = [
   {id:123, value:"value1", name:"Name1"},
   {id:124, value:"value2", name:"Name1"},
   {id:125, value:"value3", name:"Name2"},
   {id:126, value:"value4", name:"Name2"}	    
];

var array2 = []		
		
for (i=0; i<array.length;i++){						
   if (array2.indexOf(array[i].name) == -1){				
     array2.push(array[i].name);
    }
}			

console.log(array2)	


-1

Pour ceux qui recherchent un 1 liner

const names = array.reduce((acc, {name}) => acc.includes(name) ? acc : [name, ...acc], []);

ou sans utiliser de méthodes sur le prototype du tableau

const { reduce, includes } = Array;
const names = reduce(array, (acc, {name}) => includes(acc, name) ? acc : [name, ...acc], []);

pourrait être utile pour écrire des fonctions pures pour gérer cela

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);

-1
var __array=[{id:123, value:"value1", name:"Name1"},{id:124, value:"value2", name:"Name1"},{id:125, value:"value3", name:"Name2"},{id:126, value:"value4", name:"Name2"}];

function __checkArray(__obj){
    var flag = true;
    for(let i=0; i < __array.length; i++){
        if(__obj.id == __array.id){
            flag = false;
            break;
        }
    }

    return flag;
}

var __valToPush = {id: 127, value: "value5", name: "Name3"};
if(__checkArray(__valToPush)){
    __array.push(__valToPush)
}

11
avez-vous une raison particulière d'utiliser autant de traits de soulignement?
Nina Scholz
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.