Zakładając, że mam następujące:
var array =
[
{"name":"Joe", "age":17},
{"name":"Bob", "age":17},
{"name":"Carl", "age": 35}
]
Jaki jest najlepszy sposób, aby uzyskać tablicę wszystkich różnych grup wiekowych, tak aby uzyskać tablicę wyników:
[17, 35]
Czy jest jakiś sposób, w jaki mógłbym alternatywnie skonstruować dane lub lepszą metodę, tak że nie musiałbym iterować przez każdą tablicę sprawdzając wartość „age” i sprawdzać, czy istnieje inna tablica, i czy ją dodać, jeśli nie?
Gdyby istniał jakiś sposób, mogłem wyciągnąć różne epoki bez iteracji ...
Obecny nieefektywny sposób chciałbym poprawić ... Jeśli to znaczy, że zamiast „tablicy” jest tablicą obiektów, ale „mapą” obiektów z jakimś unikalnym kluczem (tj. „1,2,3”), która byłaby też dobrze. Po prostu szukam najbardziej wydajnego sposobu.
Oto jak obecnie to robię, ale dla mnie iteracja wydaje się po prostu kiepska dla wydajności, nawet jeśli działa ...
var distinct = []
for (var i = 0; i < array.length; i++)
if (array[i].age not in distinct)
distinct.push(array[i].age)
źródło
Set
przedmiotymap
są marnotrawstwem. Ta praca zajmuje tylko prosty.reduce()
etap.Odpowiedzi:
Gdyby to był PHP, zbudowałbym tablicę z kluczami i zabrałbym
array_keys
na koniec, ale JS nie ma takiego luksusu. Zamiast tego spróbuj tego:źródło
array_unique
porównałby cały przedmiot, a nie tylko wiek, o jaki tu proszono.flags = {}
jest lepszy niżflags = []
age
jest to względnie mała liczba całkowita (na pewno <120)Jeśli używasz ES6 / ES2015 lub nowszej wersji, możesz to zrobić w ten sposób:
Oto przykład, jak to zrobić.
źródło
TypeError: (intermediate value).slice is not a function
za pomocą ES6
źródło
array.filter((value, index, self) => self.map(x => x.age).indexOf(value.age) == index)
Możesz użyć takiego słownika. Zasadniczo przypisujesz wartość, którą chcesz odróżniać, jako klucz w „słowniku” (tutaj używamy tablicy jako obiektu, aby uniknąć trybu słownikowego). Jeśli klucz nie istnieje, dodajesz tę wartość jako odrębną wartość.
Oto działające demo:
Będzie to O (n), gdzie n to liczba obiektów w tablicy, a m to liczba unikalnych wartości. Nie ma szybszego sposobu niż O (n), ponieważ musisz sprawdzić każdą wartość przynajmniej raz.
Poprzednia wersja tego używała obiektu i dla in. Miały one niewielki charakter i od tego czasu zostały nieznacznie zaktualizowane powyżej. Jednak przyczyną pozornej poprawy wydajności między dwiema wersjami oryginalnego jsperf był tak mały rozmiar próbki danych. Tak więc głównym porównaniem w poprzedniej wersji była różnica między wewnętrzną mapą a użyciem filtra w porównaniu do wyszukiwania w trybie słownikowym.
Zaktualizowałem powyższy kod, jak zauważyłem, ale zaktualizowałem również jsperf, aby przeglądał 1000 obiektów zamiast 3. 3 przeoczył wiele pułapek związanych z wydajnością ( przestarzały jsperf ).
Wydajność
https://jsperf.com/filter-vs-dictionary-more-data Kiedy uruchomiłem ten słownik, był o 96% szybszy.
źródło
if( typeof(unique[array[i].age]) == "undefined"){ distinct.push(array[i].age); unique[array[i].age] = 0; }
W ten sposób rozwiązujesz ten problem za pomocą nowego zestawu za pomocą ES6 for Typescript od 25 sierpnia 2017 r
źródło
Korzystając z funkcji ES6, możesz zrobić coś takiego:
źródło
const uniqueObjects = [ ...new Set( array.map( obj => obj.age) ) ].map( age=> { return array.find(obj => obj.age === age) } )
Chciałbym po prostu zmapować i usunąć duplikaty:
Edycja: Aight! Nie najskuteczniejszy sposób pod względem wydajności, ale najprostszy i najbardziej czytelny IMO. Jeśli naprawdę zależy Ci na mikrooptymalizacji lub masz ogromne ilości danych, regularna
for
pętla będzie bardziej „wydajna”.źródło
if
s. uzyskasz bardzo różne wyniki z trzema milionami.Przykład ES6
źródło
Dla tych, którzy chcą zwrócić obiekt o wszystkich właściwościach unikatowych według klucza
źródło
Istnieje już wiele prawidłowych odpowiedzi, ale chciałem dodać tę, która używa tylko
reduce()
metody, ponieważ jest czysta i prosta.Użyj tego w ten sposób:
źródło
forEach
Wersja odpowiedzi @ Travis-j jest (pomocny w nowoczesnych przeglądarkach, a węzeł JS świata):34% szybszy na Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3
I ogólne rozwiązanie, które przyjmuje funkcję mapowania (nieco wolniej niż bezpośrednia mapa, ale jest to oczekiwane):
źródło
Domyślnie zacząłem umieszczać podkreślenie we wszystkich nowych projektach, aby nigdy nie musiałem myśleć o tych małych problemach z mungingiem danych.
Produkuje
[17, 35]
.źródło
Oto inny sposób na rozwiązanie tego:
Nie mam pojęcia, jak szybko to rozwiązanie jest porównywane z innymi, ale podoba mi się czystszy wygląd. ;-)
EDYCJA: OK, powyższe wydaje się być najwolniejszym rozwiązaniem ze wszystkich tutaj.
Stworzyłem tutaj test wydajności: http://jsperf.com/distinct-values-from-array
Zamiast testować dla grup wiekowych (liczby całkowite), postanowiłem porównać nazwy (ciągi znaków).
Metoda 1 (rozwiązanie TS) jest bardzo szybka. Co ciekawe, metoda 7 przewyższa wszystkie inne rozwiązania, tutaj właśnie pozbyłem się .indexOf () i użyłem jej „ręcznej” implementacji, unikając wywoływania zapętlonych funkcji:
Różnica w wydajności w Safari i Firefox jest niesamowita i wygląda na to, że Chrome najlepiej radzi sobie z optymalizacją.
Nie jestem do końca pewien, dlaczego powyższe fragmenty są tak szybkie w porównaniu do innych, może ktoś mądrzejszy ode mnie ma odpowiedź. ;-)
źródło
za pomocą lodash
źródło
Korzystanie z Lodash
Zwraca [17,3]
źródło
źródło
underscore.js
_.uniq(_.pluck(array,"age"))
źródło
Oto wszechstronne rozwiązanie, które wykorzystuje redukcję, pozwala na mapowanie i utrzymuje kolejność wstawiania.
items : Tablica
mapper : Unary funkcja, która mapuje element do kryteriów lub pusta, aby mapować sam element.
Stosowanie
Możesz dodać to do prototypu Array i pominąć parametr items , jeśli taki jest twój styl ...
Możesz także użyć zestawu zamiast tablicy, aby przyspieszyć dopasowanie.
źródło
źródło
Właśnie to znalazłem i pomyślałem, że jest to przydatne
Ponownie użyj podkreślenia , więc jeśli masz taki obiekt
da ci tylko unikalne przedmioty.
To, co się tutaj dzieje, polega na
indexBy
zwróceniu takiej mapyi tylko dlatego, że jest to mapa, wszystkie klucze są unikalne.
Następnie mapuję tę listę z powrotem do tablicy.
W przypadku, gdy potrzebujesz tylko odrębnych wartości
Pamiętaj, że
key
jest zwracany jako ciąg, więc jeśli potrzebujesz liczb całkowitych, powinieneś to zrobićźródło
myślę, że szukasz funkcji groupBy (za pomocą Lodash)
daje wynik:
Wersja demonstracyjna jsFiddle: http://jsfiddle.net/4J2SX/201/
źródło
Jeśli tak jak ja wolisz bardziej „funkcjonalny” bez uszczerbku dla szybkości, w tym przykładzie zastosowano szybkie wyszukiwanie słownika w celu zmniejszenia zamknięcia.
Według tego testu moje rozwiązanie jest dwa razy szybsze niż proponowana odpowiedź
źródło
źródło
Wiem, że mój kod ma małą długość i złożoność czasu, ale jest zrozumiały, więc spróbowałem w ten sposób.
Próbuję tu opracować funkcję opartą na prototypie, a kod również się zmienia.
Tutaj Distinct to moja własna funkcja prototypowa.
źródło
Jeśli masz plik Array.prototype.includes lub chcesz go wypełnić , działa to:
źródło
Mój poniższy kod pokaże unikalną tablicę wiekową, a także nową tablicę bez zduplikowanego wieku
źródło
Napisałem własną w TypeScript, na ogólny przypadek, taki jak ten w Kotlinie
Array.distinctBy {}
...U
Oczywiście gdzie jest haszowanie. W przypadku obiektów może być konieczne https://www.npmjs.com/package/es6-json-stable-stringifyźródło
W przypadku, gdy potrzebujesz unikalnego całego obiektu
[Obiekt {x: 1, y: 2}, Obiekt {x: 2, y: 1}]
źródło
Odpowiedź na to stare pytanie jest bezcelowa, ale istnieje prosta odpowiedź, która mówi o naturze Javascript. Obiekty w JavaScript są z natury tablicami mieszającymi. Możemy użyć tego, aby uzyskać skrót unikalnych kluczy:
Następnie możemy zredukować skrót do tablicy unikalnych wartości:
To wszystko czego potrzebujesz. Tablica a2 zawiera tylko unikalne epoki.
źródło
Prosty jednowarstwowy z doskonałą wydajnością. 6% szybciej niż rozwiązania ES6 w moich testach .
źródło
array.map( o => o.age).filter( (v,i,a) => a.indexOf(v)===i)
. Używam słowa kluczowego function tak rzadko teraz, że muszę czytać rzeczy dwa razy, gdy widzę go 😊