JavaScript für ungeduldige Programmierer (ES2022-Ausgabe)
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

35 Sets (Set)



Vor ES6 gab es in JavaScript keine Datenstruktur für Mengen. Stattdessen wurden zwei Workarounds verwendet

Seit ES6 gibt es in JavaScript die Datenstruktur Set, die beliebige Werte enthalten kann und Mitgliedschaftsprüfungen schnell durchführt.

35.1 Sets verwenden

35.1.1 Sets erstellen

Es gibt drei gängige Möglichkeiten, Sets zu erstellen.

Erstens können Sie den Konstruktor ohne Parameter verwenden, um ein leeres Set zu erstellen

const emptySet = new Set();
assert.equal(emptySet.size, 0);

Zweitens können Sie ein Iterable (z.B. ein Array) an den Konstruktor übergeben. Die iterierten Werte werden zu Elementen des neuen Sets

const set = new Set(['red', 'green', 'blue']);

Drittens fügt die Methode .add() Elemente zu einem Set hinzu und ist verkettbar

const set = new Set()
.add('red')
.add('green')
.add('blue');

35.1.2 Hinzufügen, Entfernen, Mitgliedschaft prüfen

.add() fügt ein Element zu einem Set hinzu.

const set = new Set();
set.add('red');

.has() prüft, ob ein Element Mitglied eines Sets ist.

assert.equal(set.has('red'), true);

.delete() entfernt ein Element aus einem Set.

assert.equal(set.delete('red'), true); // there was a deletion
assert.equal(set.has('red'), false);

35.1.3 Größe eines Sets ermitteln und es leeren

.size enthält die Anzahl der Elemente in einem Set.

const set = new Set()
  .add('foo')
  .add('bar');
assert.equal(set.size, 2)

.clear() entfernt alle Elemente eines Sets.

set.clear();
assert.equal(set.size, 0)

35.1.4 Über Sets iterieren

Sets sind iterierbar und die for-of-Schleife funktioniert wie erwartet

const set = new Set(['red', 'green', 'blue']);
for (const x of set) {
  console.log(x);
}
// Output:
// 'red'
// 'green'
// 'blue'

Wie Sie sehen, behalten Sets die Einfügungsreihenfolge bei. Das heißt, Elemente werden immer in der Reihenfolge durchlaufen, in der sie hinzugefügt wurden.

Da Sets iterierbar sind, können Sie Array.from() verwenden, um sie in Arrays zu konvertieren

const set = new Set(['red', 'green', 'blue']);
const arr = Array.from(set); // ['red', 'green', 'blue']

35.2 Beispiele für die Verwendung von Sets

35.2.1 Duplikate aus einem Array entfernen

Ein Array in ein Set und zurück zu konvertieren, entfernt Duplikate aus dem Array

assert.deepEqual(
  Array.from(new Set([1, 2, 1, 2, 3, 3, 3])),
  [1, 2, 3]);

35.2.2 Ein Set von Unicode-Zeichen (Codepunkten) erstellen

Strings sind iterierbar und können daher als Parameter für new Set() verwendet werden

assert.deepEqual(
  new Set('abc'),
  new Set(['a', 'b', 'c']));

35.3 Welche Set-Elemente gelten als gleich?

Wie bei Map-Schlüsseln werden Set-Elemente ähnlich wie === verglichen, mit der Ausnahme, dass NaN gleich sich selbst ist.

> const set = new Set([NaN, NaN, NaN]);
> set.size
1
> set.has(NaN)
true

Wie bei === werden zwei verschiedene Objekte niemals als gleich betrachtet (und es gibt derzeit keine Möglichkeit, dies zu ändern)

> const set = new Set();

> set.add({});
> set.size
1

> set.add({});
> set.size
2

35.4 Fehlende Set-Operationen

Sets fehlen mehrere gängige Operationen. Eine solche Operation kann normalerweise implementiert werden durch

35.4.1 Vereinigung (ab)

Die Vereinigung zweier Mengen a und b zu berechnen bedeutet, eine Menge zu erstellen, die die Elemente von a und b enthält.

const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
// Use spreading to concatenate two iterables
const union = new Set([...a, ...b]);

assert.deepEqual(Array.from(union), [1, 2, 3, 4]);

35.4.2 Schnittmenge (ab)

Die Schnittmenge zweier Mengen a und b zu berechnen bedeutet, eine Menge zu erstellen, die die Elemente von a enthält, die auch in b vorhanden sind.

const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const intersection = new Set(
  Array.from(a).filter(x => b.has(x))
);

assert.deepEqual(
  Array.from(intersection), [2, 3]
);

35.4.3 Differenz (a \ b)

Die Differenz zwischen zwei Mengen a und b zu berechnen bedeutet, eine Menge zu erstellen, die die Elemente von a enthält, die nicht in b sind. Diese Operation wird auch manchmal als minus (−) bezeichnet.

const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const difference = new Set(
  Array.from(a).filter(x => !b.has(x))
);

assert.deepEqual(
  Array.from(difference), [1]
);

35.4.4 Mapping über Sets

Sets haben keine Methode .map(). Aber wir können die Methode, die Arrays haben, ausleihen

const set = new Set([1, 2, 3]);
const mappedSet = new Set(
  Array.from(set).map(x => x * 2)
);

// Convert mappedSet to an Array to check what’s inside it
assert.deepEqual(
  Array.from(mappedSet), [2, 4, 6]
);

35.4.5 Filtern von Sets

Wir können Sets nicht direkt .filter(), also müssen wir die entsprechende Array-Methode verwenden

const set = new Set([1, 2, 3, 4, 5]);
const filteredSet = new Set(
  Array.from(set).filter(x => (x % 2) === 0)
);

assert.deepEqual(
  Array.from(filteredSet), [2, 4]
);

35.5 Kurzübersicht: Set<T>

35.5.1 Konstruktor

35.5.2 Set<T>.prototype: einzelne Set-Elemente

35.5.3 Set<T>.prototype: alle Set-Elemente

35.5.4 Set<T>.prototype: Iterieren und Schleifen

35.5.5 Symmetrie mit Map

Die folgenden beiden Methoden existieren hauptsächlich, damit Sets und Maps ähnliche Schnittstellen haben. Jedes Set-Element wird so behandelt, als wäre es ein Map-Eintrag, dessen Schlüssel und Wert beide das Element sind.

.entries() ermöglicht es Ihnen, ein Set in eine Map zu konvertieren

const set = new Set(['a', 'b', 'c']);
const map = new Map(set.entries());
assert.deepEqual(
  Array.from(map.entries()),
  [['a','a'], ['b','b'], ['c','c']]
);

35.6 FAQ: Sets

35.6.1 Warum haben Sets .size, während Arrays .length haben?

Die Antwort auf diese Frage finden Sie in §33.6.4 „Warum haben Maps .size, während Arrays .length haben?“.

  Quiz

Siehe Quiz-App.