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.

snoopy25
la source
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);

Nina Scholz
la source
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);

Dekel
la source
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);

Une fois je me suis battu contre un ours.
la source
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)

DavidDomaine
la source
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)

Yosvel Quintero Arguelles
la source
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);

Redu
la source
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.

user3297291
la source
3

Si vous êtes limité à ES5, j'utiliserais Lodash_.uniq

var newArray = _.uniq(array.map(function(a) {
  return a.name;
}));
AndrewHenderson
la source
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));

kkreft
la source
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>

»

Mohideen bin Mohammed
la source
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.

joeytwiddle
la source
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

Essaye ça:

nArr = [];
array.forEach((a) => {
    if (nArr.indexOf(a.name) < 0) { 
        nArr.push(a.name); 
    }
}); 
Rafael Gomes Francisco
la source
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);

BlackBeard
la source
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)	

Abhishek Gurjar
la source
-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], []);
Tyrell
la source
-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)
}
AKHIL
la source
11
avez-vous une raison particulière d'utiliser autant de traits de soulignement?
Nina Scholz