Jaki jest najskuteczniejszy sposób grupowania obiektów w tablicy?
Na przykład, biorąc pod uwagę tę tablicę obiektów:
[
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]
Wyświetlam te informacje w tabeli. Chciałbym pogrupować według różnych metod, ale chcę zsumować wartości.
Korzystam z Underscore.js do funkcji grupowania, która jest pomocna, ale nie robi to całej sztuczki, ponieważ nie chcę, aby były „dzielone”, ale „łączone”, bardziej jak group by
metoda SQL .
To, czego szukam, byłoby w stanie zsumować określone wartości (na żądanie).
Więc jeśli zrobiłem grupowanie Phase
, chciałbym otrzymać:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
A gdybym zrobił groupy Phase
/ Step
, otrzymałbym:
[
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
]
Czy istnieje przydatny skrypt do tego, czy powinienem trzymać się Underscore.js, a następnie zapętlać wynikowy obiekt, aby samodzielnie sumować?
var groupBy = function<TItem>(xs: TItem[], key: string) : {[key: string]: TItem[]} { ...
Za pomocą obiektu mapy ES6:
Informacje o mapie: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
źródło
get()
metody? czyli chcę, aby wyjście wyświetlało się bez podania kluczaconsole.log(grouped.entries());
w przykładzie jsfiddle, zwraca iterowalność, która zachowuje się jak tablica kluczy + wartości. Czy możesz spróbować i sprawdzić, czy to pomaga?console.log(Array.from(grouped));
Array.from(groupBy(jsonObj, item => i.type)).map(i => ( {[i[0]]: i[1].length} ))
z ES6:
źródło
...result
. Teraz nie mogę z tego powodu spać.Chociaż odpowiedź linq jest interesująca, jest również dość ciężka. Moje podejście jest nieco inne:
Możesz to zobaczyć w akcji na JSBin .
W Underscore nie widziałem nic, co by działało
has
, chociaż może mi tego brakować. Jest bardzo podobny_.contains
, ale używa_.isEqual
raczej niż===
do porównań. Poza tym reszta tego jest specyficzna dla problemu, chociaż z próbą bycia ogólną.Teraz
DataGrouper.sum(data, ["Phase"])
wracaI
DataGrouper.sum(data, ["Phase", "Step"])
wracaAle
sum
jest tu tylko jedna potencjalna funkcja. Możesz zarejestrować innych, jak chcesz:a teraz
DataGrouper.max(data, ["Phase", "Step"])
wrócilub jeśli to zarejestrowałeś:
następnie wywołanie
DataGrouper.tasks(data, ["Phase", "Step"])
będzie CiDataGrouper
sama w sobie jest funkcją. Możesz to nazwać danymi i listą właściwości, według których chcesz pogrupować. Zwraca tablicę, której elementy są obiektami o dwóch właściwościach:key
jest zbiorem właściwości pogrupowanych,vals
jest tablicą obiektów zawierających pozostałe właściwości nie zawarte w kluczu. Na przykładDataGrouper(data, ["Phase", "Step"])
da:DataGrouper.register
akceptuje funkcję i tworzy nową funkcję, która akceptuje początkowe dane i właściwości do grupowania według. Ta nowa funkcja przyjmuje format wyjściowy jak wyżej i uruchamia twoją funkcję kolejno dla każdej z nich, zwracając nową tablicę. Wygenerowana funkcja jest przechowywana jako właściwośćDataGrouper
według podanej przez Ciebie nazwy, a także zwracana, jeśli potrzebujesz tylko lokalnego odwołania.To wiele wyjaśnień. Mam nadzieję, że kod jest dość prosty!
źródło
Sprawdziłbym grupę lodash, ponieważ wydaje się, że robi dokładnie to, czego szukasz. Jest również dość lekki i bardzo prosty.
Przykład Fiddle: https://jsfiddle.net/r7szvt5k/
Pod warunkiem, że twoja nazwa tablicy to
arr
grupa, a z lodash jest po prostu:źródło
Prawdopodobnie łatwiej to zrobić
linq.js
, co ma być prawdziwą implementacją LINQ w JavaScript ( DEMO ):wynik:
Lub, po prostu, za pomocą selektorów opartych na łańcuchach ( DEMO ):
źródło
GroupBy(function(x){ return x.Phase; })
Możesz zbudować ES6
Map
zarray.reduce()
.Ma to kilka zalet w stosunku do innych rozwiązań:
_.groupBy()
)Map
zamiast obiektu (np. W postaci zwróconej przez_.groupBy()
). Ma to wiele zalet , w tym:Map
jest bardziej użytecznym wynikiem niż tablica tablic. Ale jeśli chcesz tablicę tablic, możesz następnie wywołaćArray.from(groupedMap.entries())
(dla tablicy[key, group array]
par) lubArray.from(groupedMap.values())
(dla prostej tablicy tablic).Jako przykład ostatniego punktu, wyobraźmy sobie, że mam tablicę obiektów, na których chcę wykonać (płytkie) scalenie według identyfikatora:
Aby to zrobić, zwykle zaczynam od pogrupowania według identyfikatora, a następnie scalenia każdej z powstałych tablic. Zamiast tego możesz wykonać scalanie bezpośrednio w
reduce()
:źródło
a.reduce(function(em, e){em.set(e.id, (em.get(e.id)||[]).concat([e]));return em;}, new Map())
, w przybliżeniu)Od: http://underscorejs.org/#groupBy
źródło
Możesz to zrobić za pomocą biblioteki JavaScript Alasql :
Spróbuj tego przykładu w jsFiddle .
BTW: Na dużych tablicach (100000 rekordów i więcej) Alasql szybciej niż Linq. Zobacz test w jsPref .
Komentarze:
źródło
źródło
MDN ma ten przykład w swojej
Array.reduce()
dokumentacji.źródło
Chociaż pytanie ma kilka odpowiedzi, a odpowiedzi wyglądają na nieco skomplikowane, sugeruję użycie JavaScript waniliowego do grupowania z zagnieżdżonym (jeśli to konieczne)
Map
.źródło
Bez mutacji:
źródło
To rozwiązanie przyjmuje dowolną funkcję (nie klucz), więc jest bardziej elastyczne niż powyższe rozwiązania i umożliwia funkcje strzałek , które są podobne do wyrażeń lambda używanych w LINQ :
UWAGA: to, czy chcesz przedłużyć
Array
prototyp, zależy od Ciebie.Przykład obsługiwany w większości przeglądarek:
Przykład użycia funkcji strzałek (ES6):
Oba powyższe przykłady zwracają:
źródło
let key = 'myKey'; let newGroupedArray = myArrayOfObjects.reduce(function (acc, val) { (acc[val[key]] = acc[val[key]] || []).push(val); return acc;});
chciałbym zasugerować moje podejście. Po pierwsze, oddzielne grupowanie i agregowanie. Deklarujmy prototypową funkcję „grupuj według”. Do wygenerowania łańcucha „haszującego” dla każdego elementu tablicy potrzebna jest inna funkcja.
po zakończeniu grupowania możesz w swoim przypadku agregować dane według potrzeb
wspólne to działa. Przetestowałem ten kod w konsoli Chrome. i nie krępuj się ulepszać i znajdować błędy;)
źródło
map[_hash(key)].key = key;
tomap[_hash(key)].key = _hash(key);
.Wyobraź sobie, że masz coś takiego:
[{id:1, cat:'sedan'},{id:2, cat:'sport'},{id:3, cat:'sport'},{id:4, cat:'sedan'}]
Robiąc to:
const categories = [...new Set(cars.map((car) => car.cat))]
Otrzymasz to:
['sedan','sport']
Objaśnienie: 1. Najpierw tworzymy nowy zestaw, przekazując tablicę. Ponieważ zestaw dopuszcza tylko unikalne wartości, wszystkie duplikaty zostaną usunięte.
Ustaw Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set Spread OperatorDoc: https://developer.mozilla.org/en-US/docs/Web/JavaScript / Reference / Operators / Spread_syntax
źródło
Sprawdzona odpowiedź - tylko płytkie grupowanie. Miło jest zrozumieć redukcję. Pytanie zawiera również problem dodatkowych obliczeń zagregowanych.
Oto PRAWDZIWA GRUPA BY dla Array of Objects według niektórych pól z 1) obliczoną nazwą klucza i 2) kompletnym rozwiązaniem do kaskadowania grupowania poprzez dostarczenie listy pożądanych kluczy i przekształcenie ich unikalnych wartości w klucze główne, takie jak SQL GROUP BY robi.
Graj z
estKey
- możesz grupować według więcej niż jednego pola, dodawać dodatkowe agregacje, obliczenia lub inne przetwarzanie.Możesz także grupować dane rekurencyjnie. Na przykład najpierw pogrupuj według
Phase
, następnie wedługStep
pól i tak dalej. Dodatkowo zdmuchnij dane dotyczące odpoczynku tłuszczu.źródło
Ten wyprowadza tablicę.
źródło
Na podstawie poprzednich odpowiedzi
i ładniej jest patrzeć na składnię rozproszenia obiektów, jeśli twoje środowisko to obsługuje.
W tym przypadku nasz reduktor przyjmuje częściowo uformowaną wartość zwracaną (zaczynając od pustego obiektu) i zwraca obiekt złożony z rozłożonych elementów poprzedniej wartości zwracanej wraz z nowym elementem, którego klucz jest obliczany na podstawie bieżącej wartości sędziego w
prop
i którego wartość jest listą wszystkich wartości tego rekwizytu wraz z bieżącą wartością.źródło
źródło
Oto nieprzyjemne, trudne do odczytania rozwiązanie za pomocą ES6:
Dla tych, którzy pytają, jak to w ogóle działa, oto wyjaśnienie:
W obu
=>
masz bezpłatnyreturn
Array.prototype.reduce
Funkcji zajmuje do 4 parametrów. Właśnie dlatego dodaje się piąty parametr, abyśmy mogli uzyskać deklarację taniej zmiennej dla grupy (k) na poziomie deklaracji parametru przy użyciu wartości domyślnej. (tak, to jest czary)Jeśli nasza bieżąca grupa nie istnieje w poprzedniej iteracji, tworzymy nową pustą tablicę.
((r[k] || (r[k] = []))
Będzie to oceniać do skrajnego lewego wyrażenia, innymi słowy, istniejącą tablicę lub pustą tablicę , dlatego jest to natychmiastpush
po tym wyrażeniu, ponieważ w obu przypadkach otrzymasz tablicę.Gdy jest znak
return
,,
operator przecinka odrzuci skrajnie lewą wartość, zwracając zmodyfikowaną poprzednią grupę dla tego scenariusza.Łatwiejsza do zrozumienia wersja, która robi to samo, to:
źródło
Pozwala wygenerować ogólne
Array.prototype.groupBy()
narzędzie. Dla urozmaicenia zastosujmy fanciness ES6, operator rozprzestrzeniania się, aby dopasować wzór Haskellesa do podejścia rekurencyjnego. Zmodyfikujmy takżeArray.prototype.groupBy()
funkcję zwrotną, która przyjmuje argument (e
) indeks (i
) i zastosowaną tablicę (a
).źródło
Odpowiedź Ceasara jest dobra, ale działa tylko dla wewnętrznych właściwości elementów wewnątrz tablicy (długość w przypadku łańcucha).
ta implementacja działa bardziej jak: ten link
mam nadzieję że to pomoże...
źródło
Z @mortb, @jmarceli odpowiedź i z tego postu ,
Korzystam z
JSON.stringify()
bycia tożsamością dla WARTOŚCI PIERWOTNEJ wielu kolumn grupy według.Bez strony trzeciej
Z firmą zewnętrzną Lodash
źródło
reduce
Wersja oparta na ES6 z obsługą funkcjiiteratee
.Działa zgodnie z oczekiwaniami, jeśli
iteratee
funkcja nie jest dostępna:W kontekście pytania PO:
Kolejna wersja ES6, która odwraca grupowanie i używa
values
askeys
orazkeys
asgrouped values
:Uwaga: funkcje są publikowane w krótkiej formie (jedna linia) dla zwięzłości i w odniesieniu do samego pomysłu. Możesz je rozwinąć i dodać dodatkowe sprawdzanie błędów itp.
źródło
Sprawdziłbym deklaratywne-js
groupBy
wydaje się robić dokładnie to, czego szukasz. To jest również:źródło
źródło
Oto wersja ES6, która nie zepsuje się na pustych elementach
źródło
Wystarczy dodać do Scotta Sauyet za odpowiedź , niektórzy ludzie pytali w komentarzach jak korzystać z jego funkcji GroupBy wartość1, wartość2, itp, zamiast grupowania tylko jedną wartość.
Wystarczy edytować jego funkcję sumowania:
pozostawiając główny (DataGrouper) bez zmian:
źródło
Z funkcją sortowania
Próba:
źródło