Was ist der effizienteste Weg, um Objekte in einem Array zu gruppieren?
Beispiel: Bei diesem Array von Objekten:
[
{ 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" }
]
Ich zeige diese Informationen in einer Tabelle an. Ich möchte nach verschiedenen Methoden gruppieren, aber ich möchte die Werte summieren.
Ich verwende Underscore.js für die groupby-Funktion, was hilfreich ist, aber nicht den ganzen Trick macht, weil ich nicht möchte, dass sie "aufgeteilt", sondern "zusammengeführt" werden, eher wie die SQL- group by
Methode.
Was ich suche, kann bestimmte Werte summieren (falls gewünscht).
Wenn ich also Groupby machen würde Phase
, würde ich Folgendes erhalten wollen:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
Und wenn ich Groupy Phase
/ machen würde Step
, würde ich erhalten:
[
{ 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 }
]
Gibt es dafür ein hilfreiches Skript, oder sollte ich mich an die Verwendung von Underscore.js halten und dann das resultierende Objekt durchlaufen, um die Summen selbst zu erstellen?
var groupBy = function<TItem>(xs: TItem[], key: string) : {[key: string]: TItem[]} { ...
Verwenden des ES6-Kartenobjekts:
Informationen zur Karte: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
quelle
get()
Methode aufzurufen ? Ich möchte, dass die Ausgabe angezeigt wird, ohne den Schlüssel zu übergebenconsole.log(grouped.entries());
in das jsfiddle-Beispiel schreibe , wird eine Iterable zurückgegeben, die sich wie ein Array von Schlüsseln + Werten verhält. Können Sie das versuchen und sehen, ob es hilft?console.log(Array.from(grouped));
Array.from(groupBy(jsonObj, item => i.type)).map(i => ( {[i[0]]: i[1].length} ))
mit ES6:
quelle
...result
. Jetzt kann ich deswegen nicht schlafen.Obwohl die Antwort von linq interessant ist, ist sie auch ziemlich schwer. Mein Ansatz ist etwas anders:
Sie können es in Aktion auf JSBin sehen .
Ich habe in Underscore nichts gesehen, was das
has
tut, obwohl ich es möglicherweise vermisse. Es ist ähnlich wie_.contains
, wird aber_.isEqual
eher als===
für Vergleiche verwendet. Davon abgesehen ist der Rest problemspezifisch, allerdings mit dem Versuch, generisch zu sein.Kehrt jetzt
DataGrouper.sum(data, ["Phase"])
zurückUnd
DataGrouper.sum(data, ["Phase", "Step"])
kehrt zurückIst
sum
aber hier nur eine mögliche Funktion. Sie können andere nach Belieben registrieren:und jetzt
DataGrouper.max(data, ["Phase", "Step"])
wird zurückkehrenoder wenn Sie dies registriert haben:
dann
DataGrouper.tasks(data, ["Phase", "Step"])
bringt dich ein AnrufDataGrouper
selbst ist eine Funktion. Sie können es mit Ihren Daten und einer Liste der Eigenschaften aufrufen, nach denen Sie gruppieren möchten. Es gibt ein Array zurück, dessen Elemente Objekte mit zwei Eigenschaften sind:key
Ist die Sammlung gruppierter Eigenschaften,vals
ist ein Array von Objekten, die die verbleibenden Eigenschaften enthalten, die nicht im Schlüssel enthalten sind. Zum BeispielDataGrouper(data, ["Phase", "Step"])
ergibt sich:DataGrouper.register
akzeptiert eine Funktion und erstellt eine neue Funktion, die die Anfangsdaten und die Eigenschaften akzeptiert, nach denen gruppiert werden soll. Diese neue Funktion nimmt dann das Ausgabeformat wie oben an und führt Ihre Funktion nacheinander für jedes von ihnen aus, wobei ein neues Array zurückgegeben wird. Die generierte Funktion wird als Eigenschaft vonDataGrouper
gemäß einem von Ihnen angegebenen Namen gespeichert und auch zurückgegeben, wenn Sie nur eine lokale Referenz wünschen.Das ist eine Menge Erklärung. Der Code ist ziemlich einfach, hoffe ich!
quelle
Ich würde lodash groupBy überprüfen, indem es genau das zu tun scheint, wonach Sie suchen. Es ist auch ziemlich leicht und sehr einfach.
Geigenbeispiel: https://jsfiddle.net/r7szvt5k/
Vorausgesetzt, Ihr Array-Name ist
arr
groupBy mit lodash ist nur:quelle
Dies ist wahrscheinlich einfacher zu bewerkstelligen
linq.js
, was eine echte Implementierung von LINQ in JavaScript ( DEMO ) sein soll:Ergebnis:
Oder einfacher mit den stringbasierten Selektoren ( DEMO ):
quelle
GroupBy(function(x){ return x.Phase; })
Sie können einen ES6
Map
aus erstellenarray.reduce()
.Dies hat einige Vorteile gegenüber den anderen Lösungen:
_.groupBy()
)Map
eher ein JavaScript als ein Objekt (z. B. wie von zurückgegeben_.groupBy()
). Dies hat viele Vorteile , einschließlich:Map
ist ein nützlicheres Ergebnis als ein Array von Arrays. Wenn Sie jedoch ein Array von Arrays möchten, können SieArray.from(groupedMap.entries())
(für ein Array von[key, group array]
Paaren) oderArray.from(groupedMap.values())
(für ein einfaches Array von Arrays) aufrufen .Stellen Sie sich als Beispiel für den letzten Punkt vor, ich habe eine Reihe von Objekten, für die ich eine (flache) Zusammenführung nach ID durchführen möchte, wie folgt:
Zu diesem Zweck beginne ich normalerweise damit, nach ID zu gruppieren und dann jedes der resultierenden Arrays zusammenzuführen. Stattdessen können Sie die Zusammenführung direkt in der
reduce()
folgenden Datei durchführen :quelle
a.reduce(function(em, e){em.set(e.id, (em.get(e.id)||[]).concat([e]));return em;}, new Map())
ungefähr)Von: http://underscorejs.org/#groupBy
quelle
Sie können dies mit der Alasql JavaScript-Bibliothek tun :
Versuchen Sie dieses Beispiel bei jsFiddle .
Übrigens: Auf großen Arrays (100000 Datensätze und mehr) Alasql schneller als Linq. Siehe Test bei jsPref .
Bemerkungen:
quelle
quelle
MDN hat dieses Beispiel in seiner
Array.reduce()
Dokumentation.quelle
Obwohl die Frage einige Antworten hat und die Antworten etwas kompliziert aussehen, empfehle ich, Vanille-Javascript für das Gruppieren mit einem verschachtelten (falls erforderlich) zu verwenden
Map
.quelle
Ohne Mutationen:
quelle
Diese Lösung verwendet eine beliebige Funktion (keine Taste), ist also flexibler als die oben genannten Lösungen und ermöglicht Pfeilfunktionen , die den in LINQ verwendeten Lambda-Ausdrücken ähneln :
HINWEIS: Ob Sie den
Array
Prototyp erweitern möchten, liegt bei Ihnen.Beispiel, das in den meisten Browsern unterstützt wird:
Beispiel mit Pfeilfunktionen (ES6):
Beide obigen Beispiele geben Folgendes zurück:
quelle
let key = 'myKey'; let newGroupedArray = myArrayOfObjects.reduce(function (acc, val) { (acc[val[key]] = acc[val[key]] || []).push(val); return acc;});
Ich möchte meinen Ansatz vorschlagen. Erstens, separate Gruppierung und Aggregation. Deklarieren wir die prototypische Funktion "Gruppieren nach". Es ist eine weitere Funktion erforderlich, um für jedes zu gruppierende Array-Element eine "Hash" -String zu erstellen.
Wenn die Gruppierung abgeschlossen ist, können Sie in Ihrem Fall Daten nach Bedarf aggregieren
gemeinsam funktioniert es. Ich habe diesen Code in Chrome Console getestet. und zögern Sie nicht, sich zu verbessern und Fehler zu finden;)
quelle
map[_hash(key)].key = key;
tomap[_hash(key)].key = _hash(key);
.Stellen Sie sich vor, Sie haben so etwas:
[{id:1, cat:'sedan'},{id:2, cat:'sport'},{id:3, cat:'sport'},{id:4, cat:'sedan'}]
Auf diese Weise:
const categories = [...new Set(cars.map((car) => car.cat))]
Sie erhalten Folgendes:
['sedan','sport']
Erläuterung: 1. Zuerst erstellen wir einen neuen Satz, indem wir ein Array übergeben. Da Set nur eindeutige Werte zulässt, werden alle Duplikate entfernt.
Legen Sie Doc fest: 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
quelle
Überprüfte Antwort - nur flache Gruppierung. Es ist ziemlich schön zu verstehen, wie man reduziert. Die Frage liefert auch das Problem zusätzlicher aggregierter Berechnungen.
Hier ist eine REAL GROUP BY für das Array von Objekten nach Feldern mit 1) berechnetem Schlüsselnamen und 2) vollständiger Lösung für die Kaskadierung von Gruppierungen, indem die Liste der gewünschten Schlüssel bereitgestellt und ihre eindeutigen Werte in Stammschlüssel wie SQL GROUP konvertiert werden BY tut.
Spielen Sie mit
estKey
- Sie können nach mehr als einem Feld gruppieren, zusätzliche Aggregationen, Berechnungen oder andere Verarbeitungen hinzufügen.Sie können Daten auch rekursiv gruppieren. Zum Beispiel zunächst gruppieren nach
Phase
, dann nachStep
Feld und so weiter. Zusätzlich die Daten zur Fettruhe abblasen.quelle
Dieser gibt ein Array aus.
quelle
Basierend auf früheren Antworten
und es ist ein wenig schöner, mit der Objektverbreitungssyntax zu betrachten, wenn Ihre Umgebung dies unterstützt.
Hier nimmt unser Reduzierer den teilweise gebildeten Rückgabewert (beginnend mit einem leeren Objekt) und gibt ein Objekt zurück, das sich aus den ausgebreiteten Elementen des vorherigen Rückgabewerts zusammen mit einem neuen Element zusammensetzt, dessen Schlüssel aus dem Wert des aktuellen iteree bei berechnet wird
prop
und dessen Wert eine Liste aller Werte für diese Requisite zusammen mit dem aktuellen Wert ist.quelle
quelle
Hier ist eine böse, schwer lesbare Lösung mit ES6:
Für diejenigen, die fragen, wie das überhaupt funktioniert, hier eine Erklärung:
In beiden
=>
hast du eine freiereturn
Die
Array.prototype.reduce
Funktion benötigt bis zu 4 Parameter. Aus diesem Grund wird ein fünfter Parameter hinzugefügt, damit wir eine billige Variablendeklaration für die Gruppe (k) auf der Ebene der Parameterdeklaration unter Verwendung eines Standardwerts haben können. (Ja, das ist Zauberei)Wenn unsere aktuelle Gruppe in der vorherigen Iteration nicht vorhanden ist, erstellen wir ein neues leeres Array.
((r[k] || (r[k] = []))
Dadurch wird der Ausdruck ganz links ausgewertet, dh ein vorhandenes Array oder ein leeres Array . Aus diesem Grund wird unmittelbarpush
nach diesem Ausdruck ein Array angezeigt In beiden Fällen erhalten Sie ein Array.Wenn es ein gibt
return
,,
verwirft der Komma- Operator den Wert ganz links und gibt die optimierte vorherige Gruppe für dieses Szenario zurück.Eine leichter verständliche Version, die dasselbe tut, ist:
quelle
Generieren wir ein generisches
Array.prototype.groupBy()
Tool. Verwenden Sie nur zur Abwechslung die ES6-Fantasie, den Spread-Operator, für einige Haskellesque-Muster, die auf einem rekursiven Ansatz übereinstimmen. Lassen Sie unsArray.prototype.groupBy()
auch einen Rückruf akzeptieren, der das Element (e
), den Index (i
) und das angewendete Array (a
) als Argumente verwendet.quelle
Ceasars Antwort ist gut, funktioniert aber nur für die inneren Eigenschaften der Elemente innerhalb des Arrays (Länge bei Zeichenfolge).
Diese Implementierung funktioniert eher wie folgt: dieser Link
hoffe das hilft...
quelle
Von @mortb, @jmarceli antworten und von diesem Beitrag ,
Ich nutze den Vorteil
JSON.stringify()
, die Identität für die PRIMITIVE VALUE mehrere Spalten der Gruppe von zu sein.Ohne Dritte
Mit Lodash -Drittanbietern
quelle
ES6-
reduce
basierte Versionsversion mit Funktionsunterstützungiteratee
.Funktioniert wie erwartet, wenn die
iteratee
Funktion nicht bereitgestellt wird:Im Kontext der OP-Frage:
Eine weitere ES6- Version, die die Gruppierung umkehrt und das
values
askeys
und daskeys
as verwendetgrouped values
:Hinweis: Funktionen werden der Kürze halber und nur in Bezug auf die Idee in ihrer Kurzform (eine Zeile) angegeben. Sie können sie erweitern und zusätzliche Fehlerprüfungen usw. hinzufügen.
quelle
Ich würde deklarative-js überprüfen,
groupBy
es scheint genau das zu tun, wonach Sie suchen. Es ist auch:quelle
quelle
Hier ist eine ES6-Version, die bei Null-Mitgliedern nicht kaputt geht
quelle
Um die Antwort von Scott Sauyet zu ergänzen, fragten einige Leute in den Kommentaren, wie man seine Funktion verwendet, um nach Wert1, Wert2 usw. zu gruppieren, anstatt nur einen Wert zu gruppieren.
Alles was es braucht ist seine Bearbeitungsfunktion zu bearbeiten:
Lassen Sie die Hauptversion (DataGrouper) unverändert:
quelle
Mit Sortierfunktion
Stichprobe:
quelle