Set und Map in JavaScript – doppelte Einträge loswerden und richtig zählen
Ein Objekt als Nachschlagetabelle geht – bis der Schlüssel eine Zahl ist, oder ein Objekt, oder zufällig "constructor" heißt. Set und Map machen genau das, wofür man Objekte bisher zweckentfremdet hat, nur ohne die Überraschungen.
Zwei eingebaute Datentypen, die viele nie benutzen, obwohl sie fast täglich passen würden.
Set – jeder Wert nur einmal
Der Klassiker: Duplikate aus einer Liste entfernen.
const tags = ['php', 'sql', 'php', 'kotlin', 'sql'];
const eindeutig = [...new Set(tags)];
// ['php', 'sql', 'kotlin']Eine Zeile, keine Schleife. Und die Reihenfolge bleibt erhalten — ein Set merkt sich, in welcher Folge die Werte hineinkamen.
Genauso nützlich ist Set als schnelle „Ist das dabei?"-Prüfung:
const gesperrt = new Set(['admin', 'root', 'system']);
if (gesperrt.has(name)) {
fehler('Dieser Benutzername ist nicht erlaubt.');
}has ist bei vielen Einträgen deutlich schneller als array.includes, weil kein Durchlauf nötig ist. Bei fünf Werten merkt man das nicht — bei fünftausend sehr wohl.
Weitere Werkzeuge: add, delete, size (kein length!) und forEach.
Map – Schlüssel, die alles sein dürfen
Ein normales Objekt als Nachschlagetabelle funktioniert meistens. Bis:
const zaehler = {};
zaehler[1] = 'eins';
Object.keys(zaehler); // ['1'] → aus der Zahl wurde TextObjektschlüssel sind immer Strings. Und dann gibt es noch geerbte Namen wie constructor oder toString, die zufällig schon existieren, obwohl du sie nie gesetzt hast.
Map hat beides nicht:
const preise = new Map();
preise.set('website', 2400);
preise.set(42, 'Antwort'); // Zahl bleibt Zahl
preise.set(kundeObjekt, 'Sonderpreis'); // sogar ein Objekt als Schlüssel
preise.get('website'); // 2400
preise.has(42); // true
preise.size; // 3Und Map merkt sich die Einfügereihenfolge zuverlässig — bei Objekten gilt das für Zahlenschlüssel nicht.
Das Muster, das ich am häufigsten brauche: zählen
const besuche = ['start', 'blog', 'start', 'kontakt', 'start'];
const anzahl = new Map();
for (const seite of besuche) {
anzahl.set(seite, (anzahl.get(seite) ?? 0) + 1);
}
// nach Häufigkeit sortiert ausgeben
[...anzahl]
.sort((a, b) => b[1] - a[1])
.forEach(([seite, n]) => console.log(`${seite}: ${n}`));
// start: 3, blog: 1, kontakt: 1Das ?? 0 fängt den ersten Durchlauf ab, in dem der Schlüssel noch nicht existiert. Mehr braucht eine Häufigkeitsauswertung nicht.
Wann trotzdem ein normales Objekt?
Wenn die Daten sowieso als JSON kommen oder als JSON weggeschickt werden. JSON.stringify(map) ergibt nämlich {} — eine Map überlebt den Weg durch JSON nicht. Dafür gibt es Object.fromEntries(map) als Umweg:
JSON.stringify(Object.fromEntries(anzahl)); // {"start":3,"blog":1,"kontakt":1}Meine Regel: Map, solange die Daten im Programm bleiben. Objekt, sobald sie über die Leitung gehen.
Du hast eine Auswertung, die bei größeren Datenmengen langsam wird? Erstaunlich oft ist es ein includes in einer Schleife. Schreib mir, wenn du dabei einen zweiten Blick brauchst.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Aufzählungen richtig verketten mit Intl.ListFormat
"a, b und c" von Hand zusammenzubauen geht fast immer schief – bei einem Element, bei zwei Elementen, beim letzten Komma. Intl.ListFormat macht es richtig, in jeder Sprache, ganz ohne Bibliothek.
requestAnimationFrame – die Animationsschleife, die zum Bildschirm passt
setInterval(schritt, 16) läuft auf einem 144-Hz-Monitor anders als auf einem alten Handy – und im Hintergrundtab einfach weiter. requestAnimationFrame löst beides, wenn man die verstrichene Zeit mitrechnet statt Bilder zu zählen.
ResizeObserver – auf die Größe eines Elements reagieren, nicht auf die des Fensters
window.onresize feuert nur, wenn sich das Fenster ändert – nicht, wenn eine Seitenleiste aufklappt oder Text nachlädt. ResizeObserver meldet dir die Größe des Elements selbst, und zwar ohne Endlosschleife.