Moderne JavaScript-Array-Methoden, die gängige Standardmuster ersetzen
Erfahren Sie, wie neuere JavaScript-Array-Methoden wie groupBy, toSorted, with und findLast umständliche Workarounds mit reduce, spread und reverse ersetzen.
Daten gruppieren ohne Bibliothek: Object.groupBy() und Map.groupBy()
Jahrelang bedeutete das Aufteilen einer Array-Liste in Kategorien auf der Grundlage einer gemeinsamen Eigenschaft, entweder einen eigenen reduce()-Akkumulator zu schreiben oder nur für diese eine Aufgabe lodash.groupBy zu verwenden.
Die ECMAScript-Spezifikation bietet nun native Gruppierungsfunktionen über Object.groupBy() und Map.groupBy().
Welches Problem dies löst
Wenn man eine flache Sammlung von Datensätzen aus einer API abruft – Transaktionen, Aufgaben, Benutzerdaten usw. – muss man diese oft in Gruppen einteilen, bevor man sie anzeigt oder darauf Geschäftslogik ausführt.
Vorher: Manuelle Verarbeitung mit reduce()
const inventory = [
{ name: 'Apples', type: 'fruit', quantity: 10 },
{ name: 'Bananas', type: 'fruit', quantity: 0 },
{ name: 'Carrots', type: 'vegetable', quantity: 14 },
{ name: 'Broccoli', type: 'vegetable', quantity: 5 }
];
const groupedByReduce = inventory.reduce((acc, item) => {
const key = item.type;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(item);
return acc;
}, {});
Nachher: klare, speziell dafür konzipierte Aufrufe zu Object.groupBy
const inventory = [
{ name: 'Apples', type: 'fruit', quantity: 10 },
{ name: 'Bananas', type: 'fruit', quantity: 0 },
{ name: 'Carrots', type: 'vegetable', quantity: 14 },
{ name: 'Broccoli', type: 'vegetable', quantity: 5 }
];
const grouped = Object.groupBy(inventory, item => item.type);
/*
Output:
{
fruit: [
{ name: 'Apples', type: 'fruit', quantity: 10 },
{ name: 'Bananas', type: 'fruit', quantity: 0 }
],
vegetable: [
{ name: 'Carrots', type: 'vegetable', quantity: 14 },
{ name: 'Broccoli', type: 'vegetable', quantity: 5 }
]
}
*/
Anstelle dessen Map.groupBy() verwenden
Falls Ihre Gruppierungsschlüssel nicht einfach nur eine Zeichenkette oder ein Symbol sind, sondern ein komplexeres Objekt oder eine dynamische Referenz, eignet sich Map.groupBy() besser:
const vipTier = { tier: 'VIP' };
const standardTier = { tier: 'Standard' };
const customers = [
{ name: 'Alice', plan: vipTier },
{ name: 'Bob', plan: standardTier },
{ name: 'Charlie', plan: vipTier }
];
const groupedByPlan = Map.groupBy(customers, user => user.plan);
console.log(groupedByPlan.get(vipTier));
// Returns Alice and Charlie's records directly keyed by reference
Unveränderliche Transformationen, denen man vertrauen kann: toSorted(), toReversed(), toSpliced()
Ein klassisches Problem in JavaScript besteht darin, dass die bekannten Methoden .sort(), .reverse() und .splice() alle das Array direkt an Ort und Stelle überschreiben, anstatt etwas Neues zurückzugeben.
In Frameworks, die auf vorhersehbarem Zustand basieren – React, Redux, Vue und ähnliche – führt die direkte Änderung eines Arrays in der Regel zu subtilen Fehlern sowie einer stillschweigend aus dem Takt geratenen Darstellung.
Welches Problem dies löst
Um diesen Fallen aus dem Weg zu gehen, schrieben Entwickler früher zunächst defensive Kopien, wie zum Beispiel [...array].sort() oder array.slice().reverse(). Die neue Methodefamilie „Array durch Kopie ändern“ beseitigt diesen gesamten Boilerplate völlig.
Vergleich der beiden Ansätze
// --- The Old Mutating Way ---
const scores = [88, 92, 79, 95];
const sortedMutated = scores.sort((a, b) => a - b);
console.log(scores); // [79, 88, 92, 95] -> Original data was destroyed!
// --- The Modern Non-Mutating Way ---
const originalScores = [88, 92, 79, 95];
const cleanSorted = originalScores.toSorted((a, b) => a - b);
console.log(originalScores); // [88, 92, 79, 95] -> Unchanged
console.log(cleanSorted); // [79, 88, 92, 95] -> Fresh array
Splice() durch toSpliced() ersetzen
toSpliced() ermöglicht es, Einträge an jeder Position zu entfernen, einzufügen oder zu ersetzen, ohne das ursprüngliche Array zu verändern:
const tabs = ['Home', 'About', 'Pricing', 'Contact'];
// Remove 'Pricing' (index 2) and insert 'Services' & 'Blog'
const updatedTabs = tabs.toSpliced(2, 1, 'Services', 'Blog');
console.log(tabs); // ['Home', 'About', 'Pricing', 'Contact']
console.log(updatedTabs); // ['Home', 'About', 'Services', 'Blog', 'Contact']
Unveränderlich ein Element aktualisieren: with()
Nehmen wir an, Sie möchten den Wert an einer bestimmten Indexposition ändern, ohne das Quellarray zu berühren. Das übliche Muster besteht darin, das Array in eine Kopie zu übertragen, den Index zu überschreiben und das Ergebnis zurückzugeben. with() fasst all das in einer klaren Ausdrucksform zusammen.
Welches Problem dies löst
Bei Aktualisierungen von Arrays muss oft ein einzelnes Element anhand seiner Position ersetzt werden, wobei das umgebende Array unverändert bleiben muss.
Vergleich der beiden Ansätze
const months = ['Jan', 'Mar', 'Mar', 'Apr'];
// Old way: Spread and mutate copy
const fixedMonthsOld = [...months];
fixedMonthsOld[1] = 'Feb';
// Modern way: array.with(index, value)
const fixedMonthsNew = months.with(1, 'Feb');
console.log(months); // ['Jan', 'Mar', 'Mar', 'Apr']
console.log(fixedMonthsNew); // ['Jan', 'Feb', 'Mar', 'Apr']
with() unterstützt ebenfalls negative Indizes, sodass das Ersetzen eines Elements von hinten genauso einfach ist:
const tags = ['v1.0.0', 'v1.1.0', 'v1.2.0-beta'];
const stableTags = tags.with(-1, 'v1.2.0');
console.log(stableTags); // ['v1.0.0', 'v1.1.0', 'v1.2.0']
Effizientes Suchen rückwärts: findLast() und findLastIndex()
Das Finden eines Elements mit .find() funktioniert gut, wenn man von vorne beginnt, doch es startet immer bei Index 0. Wenn man eigentlich das letzte Vorkommen sucht, war die übliche Lösung, das Array zunächst umzukehren – ein Ansatz, der Zeit verschwendet und das Risiko birgt, Daten zu verändern, die man eigentlich nicht anfassen wollte.
Welches Problem dies löst
Daten wie Protokolleinträge, Historie der Rückgänge, Aktivitätsfeeds und Transaktionsaufzeichnungen sind von Natur aus nach Zeit geordnet, und man möchte oft den neuesten Eintrag, der einer bestimmten Bedingung entspricht.
Vergleich der beiden Ansätze
const logs = [
{ id: 1, action: 'LOGIN', timestamp: 1000 },
{ id: 2, action: 'UPLOAD', timestamp: 1010 },
{ id: 3, action: 'ERROR', timestamp: 1020 },
{ id: 4, action: 'UPLOAD', timestamp: 1030 },
{ id: 5, action: 'LOGOUT', timestamp: 1040 }
];
// Old way: Reversing creates overhead and array allocation
const lastUploadOld = [...logs].reverse().find(log => log.action === 'UPLOAD');
// Modern way: findLast iterates backwards directly
const lastUpload = logs.findLast(log => log.action === 'UPLOAD');
const lastUploadIdx = logs.findLastIndex(log => log.action === 'UPLOAD');
console.log(lastUpload); // { id: 4, action: 'UPLOAD', timestamp: 1030 }
console.log(lastUploadIdx); // 3
Weil diese Methode von hinten nach vorne durchsucht und sofort stoppt, sobald ein Passender gefunden wird, läuft sie in O(k) Zeit ab, wobei k die Anzahl der Schritte bis zum Passenden ist – es ist nicht notwendig, zuerst den gesamten Array zu kopieren oder umzukehren.
5. Map und Filter zusammen verwenden: flatMap()
Es ist üblich, .map() mit .filter() oder .flat() zu verketten, wenn jedes Quelleteil in null, ein oder mehrere Ergebnisse umgewandelt werden kann. flatMap() kürzt diese Kette auf einen einzigen Durchlauf ab.
Was es löst: Es vermeidet das Erstellen von temporären Zwischentabellen, wenn man verschachtelte Daten umformen oder in derselben Schritt Filter anwenden und transformieren muss.
Beispiel: Flachlegen einer verschachtelten Liste von Beziehungen
const authors = [
{ name: 'Author A', books: ['Book 1', 'Book 2'] },
{ name: 'Author B', books: ['Book 3'] },
{ name: 'Author C', books: [] }
];
// Map + Flat produces 2 array allocations
const booksNested = authors.map(a => a.books).flat();
// flatMap runs the map and flattens depth-1 in one pass
const allBooks = authors.flatMap(a => a.books);
console.log(allBooks); // ['Book 1', 'Book 2', 'Book 3']
Beispiel: Transformation und Filterung zusammen
Man kann ein Element durch Rückgabe einer leeren Array entfernen und es durch Rückgabe eines eindimensionalen Arrays beibehalten:
const rawInputs = ['42', 'invalid', '100', 'undefined', '256'];
const validNumbers = rawInputs.flatMap(input => {
const num = parseInt(input, 10);
return Number.isNaN(num) ? [] : [num];
});
console.log(validNumbers); // [42, 100, 256]
6. Saubererer Zugriff auf Elemente: at()
Der Zugriff auf das letzte Element eines Arrays bedeutete traditionell die Schreibweise array[array.length - 1], was unpraktisch ist und leicht zu Fehlern führt.
at() übernimmt die Konvention negativer Indizes, wie sie aus Python bekannt ist, und ermöglicht es, sowohl bei Arrays als auch bei Zeichenketten rückwärts zu zählen.
Vorher und nachher
const queue = ['Alice', 'Bob', 'Charlie', 'Dana'];
// Old way
const lastPersonOld = queue[queue.length - 1];
const secondLastOld = queue[queue.length - 2];
// Modern way
const lastPerson = queue.at(-1); // 'Dana'
const secondLast = queue.at(-2); // 'Charlie'
Das Ergebnis ist kürzer, weniger anfällig für Fehler durch „off-by-one“-Fehler in berechneten Ausdrücken, und es funktioniert gut mit aufgereihten Methodenaufrufen, da man das Array nicht erst in einer temporären Variablen speichern muss.
Wann man darauf zurückgreifen sollte
Modernisierungs-Checkliste
- Überprüfen Sie, wie Sie den Zustand verwalten: Ersetzen Sie
.sort()und.reverse()durch.toSorted()und.toReversed(), um Fehler durch stille Mutationen zu vermeiden. - Beseitigen Sie überflüssige Reducer-Codeblöcke: Verwenden Sie stattdessen
Object.groupBy()anstelle von manuell erstellten Akkumulatorobjekten. - Erhöhen Sie die Suchgeschwindigkeit: ersetzen Sie Muster mit erstem Umkehren und anschließendem Suchen durch
findLast()für zeitlich geordnete Daten.
.at(-1) und .with(index, value) anstelle manueller Längenberechnungen und schützender Array-Kopien.Zusätzliche Literatur
- Neun gängige Muster, die unnötige React-Neuanszeigungen auslösen — Erklärt neun häufige Muster bei React-States und -Effects, die heimlich den Umfang der Neuanszeigungen erweitern, sowie wie Komponenten umstrukturiert werden können, um Updates lokal zu halten.
- Zehn verborgene Fallstricke bei React-Komponenten, die moderne Apps verlangsamen — Erfahren Sie zehn häufige Fehler bei React-Komponenten – von semantischen HTML-Lücken bis hin zu fehlender Memoisierung – sowie die notwendigen Lösungen, um Apps im Jahr 2026 schnell, zugänglich und fehlerfrei zu halten.