Usuń duplikaty z tablicy

100

Mam tablicę obiektów, która wygląda następująco:

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

Jak widać, niektóre nazwy się powtarzają. Chcę uzyskać nową tablicę tylko z nazwami, ale jeśli jakaś nazwa się powtarza, nie chcę jej ponownie dodawać. Chcę tę tablicę:

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

Próbuję to zrobić z map:

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

Problem w tym, że to zwraca:

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

Jak mogę ustawić warunek w środku map, aby nie zwracał elementu, który już istnieje? Chcę to zrobić za pomocą maplub innej funkcji ECMAScript 5 lub ECMAScript 6.

snoopy25
źródło
2
Następnie usuń duplikaty z tablicy.
Tushar
1
możliwy duplikat Remove Duplicates from JavaScript Array
Bergi
Dlaczego wiersz zawierający id: 125 nie kończy się przecinkiem?
Peter Mortensen
Należy pamiętać, że wszystkie odpowiedzi przy użyciu .indexOf () będą miały słabą wydajność, jeśli tablica jest duża, ze względu na ich złożoność czasową . Polecam używanie zestawu ES6 lub używania obiektu ES5 jako słownika.
joeytwiddle

Odpowiedzi:

211

W ES6 można było używać Setunikalnych wartości, po odwzorowaniu tylko nazw obiektów.

Ta propozycja wykorzystuje składnię rozkładówek... do gromadzenia elementów w nowej tablicy.

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
źródło
9
Dla moich informacji ... co robi ...?
Rajshekar Reddy
9
@RajshekarReddy, jest to składnia rozpowszechniania... służąca do budowania tablicy z iterowalnym obiektem.
Nina Scholz
5
Nie opowiadam się za samym „Setem”, ale raczej za sprytnym wykorzystaniem operatora spreadu. Zawsze miło jest nauczyć się czegoś nowego lub zobaczyć coś nowego na praktycznym przykładzie, więc ten post zasługuje na pochwałę.
briosheje
33
To tylko moja osobista opinia, ale dość powszechna . Użycie Array.fromznacznie lepiej oddaje zamiar konwersji (a także jest łatwiejsze do zrozumienia / wyszukania początkujących), składnia rozprzestrzeniania powinna być używana tylko wtedy, gdy rozproszony element jest jednym z wielu. Może nazwanie tego „złą praktyką” jest trochę zbyt surowe, przepraszam, mam tylko nadzieję, że nie stanie się to powszechnym idiomem.
Bergi
3
@KRyan czy ES6 jest tak nowy? Został zakończony w czerwcu 2015 roku. Czas zacząć rozumieć i korzystać z nowych funkcji
edc65
64

Jeśli szukasz rozwiązania JavaScript innego niż ES 6 (brak zestawu), możesz skorzystać z metody 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
źródło
2
Ta odpowiedź ma również tę zaletę, że nie wymaga pośrednich tablic lub zestawów, zwłaszcza zawierających same nazwy. (Przechodzi bezpośrednio z tablicy obiektów do tablicy obiektów.) +1
jpmc26
@Iwrestledabearonce, czy ten drugi parametr nie jest tym samym obiektem, co zwrócony?
Kaiido
Tak, ale nie przechodzi bezpośrednio z jednego do drugiego, jak sortowanie lub filtrowanie, jest odbudowywane z pustej tablicy.
Kiedyś walczyłem z niedźwiedziem.
1
@Dekel - Mniejsza wersja :)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
Rayon
3
@Rayon Proszę nie zwracać zbytniej uwagi na rozmiar kodu źródłowego, jest to częsta pułapka dla wielu samodzielnych programistów, a co najważniejsze, Twój program powinien być wydajny i czytelny.
liść
16

Osobiście nie rozumiem, dlaczego wszyscy polubią ES 6. Gdyby to był mój kod, wolałbym obsługiwać jak najwięcej przeglądarek.

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

Raz siłowałem się z niedźwiedziem.
źródło
2
Czy nie byłoby bardziej wydajne używanie obiektu jako słownika zamiast indexOfciągłego -ing? Np. Zrób added[name] = truei if(added[name])zamiast if(a.indexOf(name)).
Bojidar Marinov
7
"Osobiście nie rozumiem, dlaczego wszystkim się podoba es6", dlaczego zabiegasz o goto fail, przeciekając zmienną indeksu do otaczającego zakresu, itp. itd. ES6 został zakodowany w standardzie z ważnych powodów i jest w większości częściowo łatwe do wypełnienia / przekształcenia w starszych przeglądarkach.
Jared Smith
5
Bardziej czytelne? To dziwna pętla, która wypycha elementy do tablicy. Nie ma nic bardziej czytelnego niż to ......
Raz walczyłem z niedźwiedziem.
4
Szczerze mówiąc, wydaje się, że zbyt mocno się starasz znaleźć coś, na co można narzekać.
Kiedyś walczyłem z niedźwiedziem.
2
„Mam na myśli słynny błąd Apple SSL” - nie jest tak sławny, że można po prostu przejść do niego i odnieść się do niego w przypadkowych komentarzach bez kontekstu i oczekiwać, że ludzie go rozpoznają.
Hejazzman
14

Możesz też po prostu połączyć mapzfilter

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
źródło
11

Możesz użyć Object.keys (), aby uzyskać tablicę własnych wyliczalnych nazw właściwości danego obiektu z wyniku iteracji arrayzmiennej za pomocą Array.prototype.reduce (), gdzie klucze są zniszczonymi nazwami

Kod:

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
źródło
7

Wiele dobrych odpowiedzi tutaj. Chciałbym tylko wnieść swój wkład z pewną różnorodnością, mając nadzieję, że dam wam inną perspektywę.

Tablice są typu obiektowego w JavaScript, więc mogą być używane jednocześnie jako hash. Korzystając z tej funkcjonalności, możemy znacznie uprościć zadanie do wykonania w pojedynczej operacji zredukowanej o złożoności O (n) czasowej.

Jeśli nie jesteś zadowolony z tablicy zawierającej inne właściwości niż klucze tablicy, możesz również rozważyć pozostawienie oddzielnego obiektu skrótu.

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
źródło
To rozwiązanie ES5 ma dobrą wydajność, ale używanie tablicy do dwóch celów jest mylące i niebezpieczne, jeśli jedna z nazw jest tylko liczbą. Zalecałbym zrobienie tego p[c.name]na osobnym przedmiocie, oa następnie wyrzucenie go.
joeytwiddle
7

Zgadzam się, że jeśli potrzebujesz tylko namewartości, droga Setjest drogą.

Jednakże , jeśli chcesz uzyskać szereg unikatowych obiektów w oparciu o namewłaściwości, sugeruję, aby użyć Map. Szybkim sposobem na utworzenie mapy jest użycie tablicy [key, value]tablic:

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

Dodatkową korzyścią Mapjest to, że otrzymujesz zarówno filterfunkcjonalność, która eliminuje nieuniknione elementy, bez utraty połączenia z obiektami źródłowymi. Oczywiście jest to potrzebne tylko wtedy, gdy chcesz wielokrotnie odwoływać się do unikalnego zestawu obiektów.

user3297291
źródło
3

Jeśli jesteś ograniczony do ES5, użyłbym Lodash_.uniq

var newArray = _.uniq(array.map(function(a) {
  return a.name;
}));
AndrewHenderson
źródło
2

W przypadku ES6 powinno to wystarczyć.

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
źródło
11
mapma być używany z czystymi funkcjami i nie powodować skutków ubocznych. To byłaby praca dla forEachlub reduce.
Vincent van der Weele
1

Używając 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
źródło
0

W ES5 użyj obiektu jako słownika dla wydajności O ( n ).

Działa to tylko wtedy, gdy wszystkie klucze są ciągami.

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

Możesz zrobić to samo w jednym wyrażeniu, jeśli chcesz:

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

ale uważam, że rozkazujący formularz jest łatwiejszy do odczytania.

joeytwiddle
źródło
Użyłem funkcji strzałek ES6 dla większej przejrzystości. Jeśli naprawdę celujesz w ES5 bez transpilera, musisz zamiast tego użyć pełnego formularza:function (item) { return item.name; }
joeytwiddle
Alternatywnie Object.keys(), ta odpowiedź używa filter()do zachowania tylko tych nazw, które nie zostały jeszcze zapisane na mapie, co jest dość eleganckie.
joeytwiddle
0

Spróbuj tego:

nArr = [];
array.forEach((a) => {
    if (nArr.indexOf(a.name) < 0) { 
        nArr.push(a.name); 
    }
}); 
Rafael Gomes Francisco
źródło
0

Użyj array#forEach()i array#indexOf()metod takich jak ta, jeśli chcesz jeszcze maksymalną kompatybilność , zwięzłą składnię:

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

Jednakże, jeśli zgodność jest nie problem użycie ECMAScript 6 „s Setobiektu, array#maporaz Array.from()sposoby jak to:

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

Czarnobrody
źródło
0

Tak to zrobiłem, używając oddzielnej pustej tablicy.

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
źródło
-1

Dla osób poszukujących 1 wkładki

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

lub bez użycia metod na prototypie tablicy

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

może się przydać do napisania czystych funkcji do radzenia sobie z tym

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);
Tyrell
źródło
-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
źródło
11
czy masz szczególny powód, aby używać tak wielu znaków podkreślenia?
Nina Scholz,