Sets und Maps – Neue Datenstrukturen nutzen
Mit der Einführung von ES6 (ECMAScript 2015) wurden Sets und Maps als neue Datenstrukturen in JavaScript eingeführt. Diese bieten effiziente Möglichkeiten, um mit Sammlungen von Werten bzw. Schlüssel-Wert-Paaren zu arbeiten. In diesem Artikel werden wir die Grundlagen von Sets und Maps erläutern, ihre Vorteile aufzeigen und mit Codebeispielen verdeutlichen, wie sie in der Praxis eingesetzt werden können.
Was sind Sets?
Ein Set ist eine Sammlung einzigartiger Werte. Das bedeutet, dass ein Set keine doppelten Elemente enthält. Sets können Werte beliebigen Typs speichern – seien es primitive Datentypen oder Objekte.
Erstellung eines Sets
// Leeres Set erstellen
const mySet = new Set();
// Set mit initialen Werten erstellen
const numberSet = new Set([1, 2, 3, 4, 5]);
Hinzufügen und Entfernen von Elementen
const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(2); // Doppelte Werte werden ignoriert
console.log(mySet); // Set { 1, 2 }
mySet.delete(1);
console.log(mySet); // Set { 2 }
Überprüfung auf Vorhandensein von Elementen
const mySet = new Set([1, 2, 3]);
console.log(mySet.has(2)); // true
console.log(mySet.has(4)); // false
Iteration über ein Set
const mySet = new Set(['a', 'b', 'c']);
for (let item of mySet) {
console.log(item);
}
// Ausgabe:
// 'a'
// 'b'
// 'c'
Anwendungsbeispiel: Duplikate aus einem Array entfernen
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
Was sind Maps?
Eine Map ist eine Sammlung von Schlüssel-Wert-Paaren. Im Gegensatz zu normalen Objekten können Maps Schlüssel beliebigen Typs haben, nicht nur Strings.
Erstellung einer Map
// Leere Map erstellen
const myMap = new Map();
// Map mit initialen Werten erstellen
const fruitMap = new Map([
['Apfel', 1],
['Banane', 2],
['Orange', 3]
]);
Hinzufügen und Entfernen von Einträgen
const myMap = new Map();
myMap.set('Name', 'Max');
myMap.set('Alter', 28);
console.log(myMap);
// Map { 'Name' => 'Max', 'Alter' => 28 }
myMap.delete('Alter');
console.log(myMap);
// Map { 'Name' => 'Max' }
Zugriff auf Werte
const myMap = new Map();
myMap.set('Schlüssel', 'Wert');
console.log(myMap.get('Schlüssel')); // 'Wert'
console.log(myMap.has('Schlüssel')); // true
Iteration über eine Map
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
for (let [key, value] of myMap) {
console.log(`${key}: ${value}`);
}
// Ausgabe:
// 'a: 1'
// 'b: 2'
Anwendungsbeispiel: Objekte als Schlüssel verwenden
const user1 = { name: 'Anna' };
const user2 = { name: 'Ben' };
const loginCount = new Map();
loginCount.set(user1, 5);
loginCount.set(user2, 3);
console.log(loginCount.get(user1)); // 5
console.log(loginCount.get(user2)); // 3
Unten findest du die zuvor genannten Vorteile und Schwächen von Sets und Maps, nun ergänzt um kurze, praxisnahe Codebeispiele, die die jeweiligen Punkte veranschaulichen.
Vorteile von Sets und Maps
Sets
-
Einfaches Entfernen von Duplikaten aus Arrays:
const numbers = [1, 2, 2, 3, 4, 4, 5]; const uniqueNumbers = [...new Set(numbers)]; console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
Hier nutzt du ein Set, um doppelte Werte aus einem Array zu entfernen. Da ein Set automatisch alle Duplikate ignoriert, erhältst du mit einer simplen Umwandlung ein Array ohne Mehrfacheinträge.
-
Effiziente Überprüfung auf das Vorhandensein von Elementen (O(1) Komplexität):
const mySet = new Set(['Apfel', 'Banane', 'Kirsche']); console.log(mySet.has('Banane')); // true, schnelle Abfrage in O(1) console.log(mySet.has('Orange')); // false
Mit
has()
kannst du schnell überprüfen, ob ein Element im Set vorhanden ist. Im Gegensatz zu Arrays ist die Überprüfung durchschnittlich in O(1) möglich, was bei großen Datenmengen einen erheblichen Performancevorteil bietet.
Maps
-
Schlüssel können beliebigen Typs sein (nicht nur Strings):
const objKey = { id: 42 }; const map = new Map(); map.set(objKey, 'Wert für ein Objekt als Schlüssel'); map.set(123, 'Wert für eine Zahl als Schlüssel'); console.log(map.get(objKey)); // "Wert für ein Objekt als Schlüssel" console.log(map.get(123)); // "Wert für eine Zahl als Schlüssel"
Hier wird sowohl ein Objekt als auch eine Zahl als Schlüssel genutzt. Während herkömmliche Objekte in JavaScript nur Strings oder Symbole als Schlüssel ermöglichen, sind Maps deutlich flexibler.
-
Behalten die Einfügereihenfolge bei:
const orderedMap = new Map([ ['erstes', 1], ['zweites', 2], ['drittes', 3] ]); for (const [key, value] of orderedMap) { console.log(key, value); } // Ausgabe in Einfügereihenfolge: // "erstes" 1 // "zweites" 2 // "drittes" 3
Maps garantieren, dass beim Iterieren der Einfügereihenfolge gefolgt wird, was bei normalen Objekten nicht garantiert ist.
-
Bessere Leistung bei häufigen Einfügungen und Löschungen von Schlüssel-Wert-Paaren:
const largeMap = new Map(); // Häufige Einfügungen for (let i = 0; i < 100000; i++) { largeMap.set(`key${i}`, i); } console.log(largeMap.size); // 100000 - zeigt, dass große Datenmengen performant verwaltet werden // Schnelles Löschen largeMap.delete('key500'); console.log(largeMap.has('key500')); // false
Schwächen und Grenzen
Sets
-
Keine direkte Möglichkeit, auf ein Element über seinen Index zuzugreifen:
const fruits = new Set(['Apfel', 'Banane', 'Kirsche']); // Es gibt kein fruits[1] o.Ä. // Stattdessen musst du z. B. über alle Elemente iterieren: for (const fruit of fruits) { console.log(fruit); } // Ausgabe (Reihenfolge nicht garantiert): "Apfel", "Banane", "Kirsche"
Sets bieten keine Array-ähnliche Indexierung. Du kannst die Elemente nur als Ganzes iterieren oder die Existenz prüfen, aber nicht einfach das “zweite” Element auslesen.
-
Sind ungeordnet, d.h. die Reihenfolge der Elemente ist nicht garantiert:
const letters = new Set(['b', 'a', 'c']); // Beim Iterieren erhältst du meist die Einfügereihenfolge, aber garantieren kann man das formell nicht. for (const letter of letters) { console.log(letter); } // Kann z. B. "b", "a", "c" ausgeben, aber es ist per Definition nicht sicher garantiert.
Obwohl die meisten Implementierungen die Einfügereihenfolge beibehalten, schreibt der Standard das nicht zwingend vor. Man sollte sich also nicht auf eine bestimmte Reihenfolge verlassen.
Maps
-
Sind nicht serialisierbar mit JSON.stringify:
const myMap = new Map([['name', 'Anna'], ['alter', 25]]); console.log(JSON.stringify(myMap)); // "{}" - Map wird nicht sinnvoll serialisiert, weil sie nicht der JSON-Struktur entspricht. // Man muss explizit konvertieren: const obj = Object.fromEntries(myMap); console.log(JSON.stringify(obj)); // '{"name":"Anna","alter":25}' – jetzt sinnvoll serialisierbar
Wenn du Datenpersistenz benötigst, musst du deine Map zuerst in ein reguläres Objekt oder Array konvertieren, bevor du
JSON.stringify()
darauf anwendest. -
Wenn man nur Strings oder Symbole als Schlüssel verwendet, bieten normale Objekte möglicherweise eine einfachere Alternative:
// Wenn du nur String-Schlüssel nutzt, reicht ein normales Objekt: const person = { name: 'Ben', alter: 30 }; console.log(person.name); // "Ben" // Hier ist eine Map nicht unbedingt nötig, da das Objekt den Zweck bereits erfüllt.
Normale Objekte sind oft simpler zu nutzen, wenn die Anforderungen an Schlüssel nicht über Strings oder Symbole hinausgehen und man weder Einfügereihenfolge noch komplexe Operationen aufwändig verwalten muss.
Praktische Anwendungsfälle
Zum Abschluss noch ein paar praktische Anwendungsbeispiele für die Benutzung von Sets und Maps.
1. Benutzeraktivitäten mit Sets verfolgen
const activeUsers = new Set();
function login(user) {
activeUsers.add(user);
}
function logout(user) {
activeUsers.delete(user);
}
const user1 = { name: 'Max' };
const user2 = { name: 'Lisa' };
login(user1);
login(user2);
console.log(activeUsers.size); // 2
logout(user1);
console.log(activeUsers.size); // 1
In diesem Codebeispiel wird ein Set namens activeUsers erstellt, um die aktuell angemeldeten Benutzer zu speichern. Ein Set ist eine spezielle Art von Sammlung in JavaScript, die nur eindeutige Werte speichert.
Die Funktion login nimmt ein Benutzerobjekt als Argument und fügt es dem activeUsers-Set hinzu. Die Funktion logout entfernt ein Benutzerobjekt aus dem activeUsers-Set.
Zwei Benutzerobjekte user1 und user2 werden erstellt, die jeweils die Namen Max und Lisa haben. Beide Benutzer werden durch Aufrufen der login-Funktion angemeldet und dem activeUsers-Set hinzugefügt.
Nach dem Anmelden beider Benutzer wird die Größe des activeUsers-Sets mit console.log(activeUsers.size) ausgegeben, was 2 ergibt, da zwei Benutzer angemeldet sind.
Anschließend wird user1 durch Aufrufen der logout-Funktion abgemeldet und aus dem activeUsers-Set entfernt. Die Größe des activeUsers-Sets wird erneut ausgegeben, was nun 1 ergibt, da nur noch ein Benutzer angemeldet ist.
Dieses Beispiel zeigt, wie man ein Set verwenden kann, um eine Sammlung eindeutiger Werte zu verwalten und wie man Benutzerobjekte zu einem Set hinzufügt und daraus entfernt.
2. Keyword-Zählung mit Maps
const text = 'Hallo Welt Hallo JavaScript Welt';
const words = text.split(' ');
const wordCount = new Map();
for (let word of words) {
const count = wordCount.get(word) || 0;
wordCount.set(word, count + 1);
}
for (let [word, count] of wordCount) {
console.log(`${word}: ${count}`);
}
// Ausgabe:
// 'Hallo: 2'
// 'Welt: 2'
// 'JavaScript: 1'
In diesem Codebeispiel wird ein Textstring text definiert, der die Wörter “Hallo Welt Hallo JavaScript Welt” enthält. Der Text wird mit der Methode split(’ ’) in ein Array von Wörtern aufgeteilt, wobei jedes Wort ein Element des Arrays words wird.
Ein Map-Objekt namens wordCount wird erstellt, um die Häufigkeit jedes Wortes im Text zu speichern. Eine Map ist eine Sammlung von Schlüssel-Wert-Paaren, bei der jeder Schlüssel nur einmal vorkommen kann.
In einer for-Schleife wird jedes Wort im Array words durchlaufen. Für jedes Wort wird die aktuelle Anzahl der Vorkommen aus der wordCount-Map abgerufen. Wenn das Wort noch nicht in der Map vorhanden ist, wird 0 zurückgegeben. Die Anzahl der Vorkommen wird um eins erhöht und in der Map gespeichert.
In einer weiteren for-Schleife werden die Schlüssel-Wert-Paare der wordCount-Map durchlaufen. Für jedes Paar wird das Wort und die Anzahl der Vorkommen mit console.log ausgegeben.
Die Ausgabe zeigt, wie oft jedes Wort im ursprünglichen Text vorkommt:
“Hallo” kommt zweimal vor. “Welt” kommt zweimal vor. “JavaScript” kommt einmal vor. Dieses Beispiel zeigt, wie man die Häufigkeit von Wörtern in einem Text mithilfe von split, Map und Schleifen in JavaScript ermitteln kann.
Fazit
Sets und Maps sind wertvolle Ergänzungen zu den vorhandenen Datenstrukturen in JavaScript. Sie bieten effiziente und flexible Möglichkeiten, um mit Sammlungen von Werten und Schlüssel-Wert-Paaren zu arbeiten. Durch ihre einzigartigen Eigenschaften können sie in vielen Situationen herkömmliche Arrays und Objekte ergänzen oder sogar ersetzen.
Nutze Sets und Maps in deinem nächsten Projekt, um von ihren Vorteilen zu profitieren und deinen Code effizienter und lesbarer zu gestalten!