Entfernen Sie Duplikate aus einem Array

100

Ich habe eine Reihe von Objekten, die so aussehen:

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"}
    ...
];

Wie Sie sehen können, werden einige Namen wiederholt. Ich möchte ein neues Array nur mit Namen erhalten, aber wenn sich ein Name wiederholt, möchte ich ihn nicht erneut hinzufügen. Ich möchte dieses Array:

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

Ich versuche das mit map:

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

Das Problem ist jedoch, dass dies zurückkehrt:

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

Wie kann ich eine Bedingung festlegen map, damit kein bereits vorhandenes Element zurückgegeben wird? Ich möchte dies mit mapoder einer anderen ECMAScript 5- oder ECMAScript 6-Funktion tun .

snoopy25
quelle
2
Entfernen Sie dann die Duplikate aus dem Array.
Tushar
1
Mögliches Duplikat von Duplikate aus JavaScript-Array entfernen
Bergi
Warum endet die Zeile mit id: 125 nicht mit einem Komma?
Peter Mortensen
Bitte beachten Sie, dass alle Antworten mit .indexOf () aufgrund ihrer quadratischen Zeitkomplexität eine schlechte Leistung aufweisen, wenn das Array groß ist . Ich würde empfehlen, ES6 Set oder ein ES5-Objekt als Wörterbuch zu verwenden.
Joeytwiddle

Antworten:

210

Mit ES6 können Sie Seteindeutige Werte verwenden, nachdem Sie nur die Namen der Objekte zugeordnet haben.

Dieser Vorschlag verwendet eine Spread-Syntax... zum Sammeln der Elemente in einem neuen Array.

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
quelle
9
Zu meiner Information .. was macht ...das?
Rajshekar Reddy
9
@RajshekarReddy, es ist eine verbreitete Syntax... zum Erstellen eines Arrays mit einem iterierbaren Objekt.
Nina Scholz
5
Ich stimme nicht für das "Set" selbst, sondern für den cleveren Einsatz des Spread-Operators. Es ist immer schön, etwas Neues zu lernen oder etwas Neues auf ein praktisches Beispiel anzuwenden, daher verdient dieser Beitrag eine positive Bewertung.
Briosheje
33
Es ist nur meine persönliche Meinung, aber eine ziemlich häufige . Die Verwendung Array.fromvermittelt die Absicht der Konvertierung viel besser (und ist auch leichter zu verstehen / nach Neulingen zu suchen). Die Spread-Syntax sollte nur verwendet werden, wenn das verteilte Element eines von vielen ist. Vielleicht ist es ein bisschen zu hart, es als "schlechte Praxis" zu bezeichnen. Tut mir leid, ich hoffe nur, dass es nicht zu einer gängigen Redewendung wird.
Bergi
3
@KRyan ist ES6 so neu? Es wurde im Juni 2015 abgeschlossen. Es ist Zeit, die neuen Funktionen zu verstehen und zu nutzen
edc65
64

Wenn Sie nach einer JavaScript-Lösung suchen, die nicht ES 6 (no Set) ist, können Sie die reduceMethode des Arrays verwenden :

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
quelle
2
Diese Antwort hat auch den Vorteil, dass keine Zwischenarrays oder -sätze erforderlich sind, insbesondere solche, die nur die Namen selbst enthalten. (Es geht direkt von Array von Objekten zu Array von Objekten.) +1
jpmc26
@Iwrestledabearonce, ist dieser zweite Parameter nicht dasselbe Objekt wie der zurückgegebene?
Kaiido
Ja, aber es geht nicht wie Sortieren oder Filtern direkt von einem zum anderen, es wird aus einem leeren Array neu erstellt.
Ich habe einmal mit einem Bären gerungen.
1
@ Dekel - Kleinere Version :)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
Rayon
3
@Rayon Bitte achten Sie nicht zu sehr auf die Größe des Quellcodes. Dies ist eine häufige Gefahr für viele selbst erstellte Programmierer. Vor allem sollte Ihr Programm effizient und lesbar sein.
Blatt
16

Persönlich verstehe ich nicht, warum alle Lust auf ES 6 haben. Wenn es mein Code wäre, würde ich es vorziehen, so viele Browser wie möglich zu unterstützen.

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

I habe einmal mit einem Bär gekämpft.
quelle
2
Wird es nicht performanter sein, ein Objekt als Wörterbuch zu verwenden, anstatt indexOfständig zu arbeiten? ZB tun added[name] = trueund if(added[name])statt if(a.indexOf(name)).
Bojidar Marinov
7
"Ich persönlich verstehe nicht, warum alle Lust auf es6 haben" Warum wirben Sie um eine goto fail, verlieren Ihre Indexvariable in den umschließenden Bereich usw. usw. ES6 wurde aus guten Gründen in den Standard kodifiziert und ist für die meisten Teil für ältere Browser trivial einfach zu füllen / zu transformieren.
Jared Smith
5
Besser lesbar? Es ist eine verdammte for-Schleife, die Elemente auf ein Array schiebt. Viel lesbarer geht es nicht ......
Ich habe einmal mit einem Bären gerungen.
4
Ganz ehrlich, es fühlt sich einfach so an, als ob Sie sich viel zu sehr bemühen, etwas zu finden, über das Sie sich beschweren können.
Ich habe einmal mit einem Bären gerungen.
2
"Ich beziehe mich auf den berühmten Apple SSL-Fehler" - Es ist nicht so berühmt, dass Sie einfach in zufälligen Kommentaren außerhalb des Kontexts darauf verweisen und erwarten können, dass die Leute ihn erkennen.
Hejazzman
14

Sie könnten auch einfach kombinieren mapmitfilter

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)

DavidDomain
quelle
11

Sie können Object.keys () verwenden , um das Array der eigenen aufzählbaren Eigenschaftsnamen eines bestimmten Objekts aus dem Objektergebnis der iterierenden arrayVariablen mit Array.prototype.reduce () abzurufen, wobei die Schlüssel die zerstörten Namen sind

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

Viele gute Antworten hier. Ich möchte nur mit etwas Abwechslung dazu beitragen, Ihnen eine andere Perspektive zu geben.

Arrays sind in JavaScript vom Objekttyp, sodass sie gleichzeitig als Hash verwendet werden können. Durch die Verwendung dieser Funktionalität können wir die Arbeit in einem einzigen Reduktionsvorgang mit O (n) -Zeitkomplexität erheblich vereinfachen.

Wenn Sie nicht zufrieden sind, dass Ihr Array andere Eigenschaften als die Array-Schlüssel enthält, können Sie auch ein separates Hash-Objekt behalten.

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

Reduzieren
quelle
Diese ES5-Lösung hat eine gute Leistung, aber die Verwendung des Arrays für zwei Zwecke ist verwirrend und gefährlich, wenn einer der Namen nur eine Zahl ist. Ich würde empfehlen, das p[c.name]Zeug auf einem separaten Objekt zu machen ound es anschließend zu verwerfen.
Joeytwiddle
7

Ich bin damit einverstanden, dass a der namerichtige SetWeg ist , wenn Sie nur die Werte benötigen .

Allerdings , wenn Sie wollen auf der eine Reihe von einzigartigen Objekten erhalten basierte nameEigenschaft, würde ich vorschlagen , eine verwenden Map. Eine schnelle Möglichkeit zum Erstellen einer Karte besteht in einer Reihe von [key, value]Arrays:

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

Ein zusätzlicher Vorteil von Mapist, dass Sie beide filterFunktionen erhalten, die die Nicht-Unikate ausschneiden, ohne die Verbindung mit den Quellobjekten zu verlieren. Natürlich ist es nur erforderlich, wenn Sie mehrmals auf den eindeutigen Satz von Objekten verweisen müssen.

user3297291
quelle
3

Wenn Sie auf ES5 beschränkt sind, würde ich Lodashs verwenden_.uniq

var newArray = _.uniq(array.map(function(a) {
  return a.name;
}));
AndrewHenderson
quelle
2

Mit ES6 sollte dies den Job machen.

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
quelle
11
mapsoll mit reinen Funktionen verwendet werden, keine Nebenwirkungen haben. Dies wäre ein Job für forEachoder reduce.
Vincent van der Weele
1

Verwenden von 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
quelle
0

Verwenden Sie in ES5 ein Objekt als Wörterbuch für die O ( n ) -Leistung.

Dies funktioniert nur, wenn alle Schlüssel Strings sind.

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

Sie können dasselbe in einem Ausdruck tun, wenn Sie möchten:

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

aber ich finde die imperative Form leichter zu lesen.

joeytwiddle
quelle
Aus Gründen der Übersichtlichkeit habe ich ES6-Pfeilfunktionen verwendet. Wenn Sie wirklich auf ES5 ohne Transpiler abzielen, müssen Sie stattdessen das vollständige Formular verwenden:function (item) { return item.name; }
joeytwiddle
Als Alternative zu Object.keys(), diese Antwort Verwendungen filter()zu halten diese Namen nur , die noch nicht in der Karte gespeichert sind, die sehr elegant ist.
Joeytwiddle
0

Versuche dies:

nArr = [];
array.forEach((a) => {
    if (nArr.indexOf(a.name) < 0) { 
        nArr.push(a.name); 
    }
}); 
Rafael Gomes Francisco
quelle
0

Verwenden Sie array#forEach()und array#indexOf()Methoden wie diese, wenn Sie noch maximale Kompatibilität wünschen , prägnante Syntax:

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

Wenn die Kompatibilität jedoch kein Problem darstellt, verwenden Sie das Objekt von ECMAScript 6 und folgende Methoden: Setarray#mapArray.from()

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

So habe ich es gemacht, indem ich ein separates leeres Array verwendet habe.

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

Für diejenigen, die einen 1 Liner suchen

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

oder ohne Verwendung von Methoden für den Prototyp des Arrays

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

könnte nützlich sein, um einige reine Funktionen zu schreiben, um damit umzugehen

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);
Tyrell
quelle
-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
quelle
11
Haben Sie einen besonderen Grund, so viele Unterstriche zu verwenden?
Nina Scholz