MWCodebymw.de ↗
JavaScript

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 Text

Objektschlü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;             // 3

Und 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: 1

Das ?? 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

#JavaScript#Set#Map#Arrays#Duplikate

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 →