Map)Map<K,V>Vor ES6 gab es in JavaScript keine Datenstruktur für Wörterbücher, und Objekte wurden als Wörterbücher von Strings zu beliebigen Werten (miss-)braucht. ES6 brachte Maps, die Wörterbücher von beliebigen Werten zu beliebigen Werten sind.
Eine Instanz von Map ordnet Schlüssel Werten zu. Eine einzelne Schlüssel-Wert-Zuordnung wird als Eintrag bezeichnet.
Es gibt drei gängige Arten, Maps zu erstellen.
Erstens können Sie den Konstruktor ohne Parameter verwenden, um eine leere Map zu erstellen
const emptyMap = new Map();
assert.equal(emptyMap.size, 0);Zweitens können Sie dem Konstruktor ein Iterable (z. B. ein Array) von Schlüssel-Wert-„Paaren“ (Arrays mit zwei Elementen) übergeben
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'], // trailing comma is ignored
]);Drittens fügt die Methode .set() Einträge zu einer Map hinzu und ist verkettbar
const map = new Map()
.set(1, 'one')
.set(2, 'two')
.set(3, 'three');Wie wir später sehen werden, sind Maps auch Iterables über Schlüssel-Wert-Paare. Daher können Sie den Konstruktor verwenden, um eine Kopie einer Map zu erstellen. Diese Kopie ist flach: Schlüssel und Werte sind dieselben; sie werden nicht dupliziert.
const original = new Map()
.set(false, 'no')
.set(true, 'yes');
const copy = new Map(original);
assert.deepEqual(original, copy);.set() und .get() dienen zum Schreiben und Lesen von Werten (anhand von Schlüsseln).
const map = new Map();
map.set('foo', 123);
assert.equal(map.get('foo'), 123);
// Unknown key:
assert.equal(map.get('bar'), undefined);
// Use the default value '' if an entry is missing:
assert.equal(map.get('bar') ?? '', '');.has() prüft, ob eine Map einen Eintrag mit einem bestimmten Schlüssel hat. .delete() entfernt Einträge.
const map = new Map([['foo', 123]]);
assert.equal(map.has('foo'), true);
assert.equal(map.delete('foo'), true)
assert.equal(map.has('foo'), false).size enthält die Anzahl der Einträge in einer Map. .clear() entfernt alle Einträge einer Map.
const map = new Map()
.set('foo', true)
.set('bar', false)
;
assert.equal(map.size, 2)
map.clear();
assert.equal(map.size, 0).keys() gibt ein Iterable über die Schlüssel einer Map zurück
const map = new Map()
.set(false, 'no')
.set(true, 'yes')
;
for (const key of map.keys()) {
console.log(key);
}
// Output:
// false
// trueWir verwenden Array.from(), um das von .keys() zurückgegebene Iterable in ein Array zu konvertieren
assert.deepEqual(
Array.from(map.keys()),
[false, true]);.values() funktioniert wie .keys(), nur für Werte anstelle von Schlüsseln.
.entries() gibt ein Iterable über die Einträge einer Map zurück
const map = new Map()
.set(false, 'no')
.set(true, 'yes')
;
for (const entry of map.entries()) {
console.log(entry);
}
// Output:
// [false, 'no']
// [true, 'yes']Array.from() konvertiert das von .entries() zurückgegebene Iterable in ein Array
assert.deepEqual(
Array.from(map.entries()),
[[false, 'no'], [true, 'yes']]);Map-Instanzen sind auch Iterables über Einträge. Im folgenden Code verwenden wir Destrukturierung, um auf die Schlüssel und Werte von map zuzugreifen
for (const [key, value] of map) {
console.log(key, value);
}
// Output:
// false, 'no'
// true, 'yes'Maps zeichnen auf, in welcher Reihenfolge Einträge erstellt wurden, und respektieren diese Reihenfolge, wenn Einträge, Schlüssel oder Werte aufgelistet werden
const map1 = new Map([
['a', 1],
['b', 2],
]);
assert.deepEqual(
Array.from(map1.keys()), ['a', 'b']);
const map2 = new Map([
['b', 2],
['a', 1],
]);
assert.deepEqual(
Array.from(map2.keys()), ['b', 'a']);Solange eine Map nur Strings und Symbole als Schlüssel verwendet, können Sie sie in ein Objekt konvertieren (über Object.fromEntries())
const map = new Map([
['a', 1],
['b', 2],
]);
const obj = Object.fromEntries(map);
assert.deepEqual(
obj, {a: 1, b: 2});Sie können auch ein Objekt mit String- oder Symbolschlüsseln in eine Map konvertieren (über Object.entries())
const obj = {
a: 1,
b: 2,
};
const map = new Map(Object.entries(obj));
assert.deepEqual(
map, new Map([['a', 1], ['b', 2]]));countChars() gibt eine Map zurück, die Zeichen auf die Anzahl ihrer Vorkommen abbildet.
function countChars(chars) {
const charCounts = new Map();
for (let ch of chars) {
ch = ch.toLowerCase();
const prevCount = charCounts.get(ch) ?? 0;
charCounts.set(ch, prevCount+1);
}
return charCounts;
}
const result = countChars('AaBccc');
assert.deepEqual(
Array.from(result),
[
['a', 2],
['b', 1],
['c', 3],
]
);Jeder Wert kann ein Schlüssel sein, auch ein Objekt
const map = new Map();
const KEY1 = {};
const KEY2 = {};
map.set(KEY1, 'hello');
map.set(KEY2, 'world');
assert.equal(map.get(KEY1), 'hello');
assert.equal(map.get(KEY2), 'world');Die meisten Map-Operationen müssen prüfen, ob ein Wert gleich einem der Schlüssel ist. Dies geschieht über die interne Operation SameValueZero, die wie === funktioniert, aber NaN als gleich zu sich selbst betrachtet.
Daher können Sie NaN in Maps als Schlüssel verwenden, genau wie jeden anderen Wert
> const map = new Map();
> map.set(NaN, 123);
> map.get(NaN)
123Unterschiedliche Objekte werden immer als unterschiedlich betrachtet. Das ist etwas, das (noch) nicht geändert werden kann – die Konfiguration der Schlüsselgleichheit steht auf der langfristigen Roadmap von TC39.
> new Map().set({}, 1).set({}, 2).size
2Sie können ein Array .map() und .filter(), aber es gibt keine solchen Operationen für eine Map. Die Lösung ist
Ich werde die folgende Map verwenden, um zu demonstrieren, wie das funktioniert.
const originalMap = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');Abbildung von originalMap
const mappedMap = new Map( // step 3
Array.from(originalMap) // step 1
.map(([k, v]) => [k * 2, '_' + v]) // step 2
);
assert.deepEqual(
Array.from(mappedMap),
[[2,'_a'], [4,'_b'], [6,'_c']]);Filterung von originalMap
const filteredMap = new Map( // step 3
Array.from(originalMap) // step 1
.filter(([k, v]) => k < 3) // step 2
);
assert.deepEqual(Array.from(filteredMap),
[[1,'a'], [2,'b']]);Array.from() konvertiert jedes Iterable in ein Array.
Es gibt keine Methoden zum Kombinieren von Maps, weshalb wir einen Workaround verwenden müssen, der dem aus dem vorherigen Abschnitt ähnelt.
Kombinieren wir die folgenden beiden Maps
const map1 = new Map()
.set(1, '1a')
.set(2, '1b')
.set(3, '1c')
;
const map2 = new Map()
.set(2, '2b')
.set(3, '2c')
.set(4, '2d')
;Um map1 und map2 zu kombinieren, erstellen wir ein neues Array und verteilen (...) die Einträge (Schlüssel-Wert-Paare) von map1 und map2 hinein (durch Iteration). Dann konvertieren wir das Array zurück in eine Map. All das geschieht in Zeile A
const combinedMap = new Map([...map1, ...map2]); // (A)
assert.deepEqual(
Array.from(combinedMap), // convert to Array for comparison
[ [ 1, '1a' ],
[ 2, '2b' ],
[ 3, '2c' ],
[ 4, '2d' ] ]
); Übung: Zwei Maps kombinieren
exercises/maps/combine_maps_test.mjs
Map<K,V>Hinweis: Der Kürze halber tue ich so, als hätten alle Schlüssel denselben Typ K und alle Werte denselben Typ V.
new Map<K, V>(entries?: Iterable<[K, V]>) [ES6]
Wenn Sie den Parameter entries nicht angeben, wird eine leere Map erstellt. Wenn Sie ein Iterable von [Schlüssel, Wert]-Paaren angeben, werden diese Paare als Einträge zur Map hinzugefügt. Zum Beispiel
const map = new Map([
[ 1, 'one' ],
[ 2, 'two' ],
[ 3, 'three' ], // trailing comma is ignored
]);Map<K,V>.prototype: Umgang mit einzelnen Einträgen.get(key: K): V [ES6]
Gibt den Wert zurück, dem key in dieser Map zugeordnet ist. Wenn kein Schlüssel key in dieser Map vorhanden ist, wird undefined zurückgegeben.
const map = new Map([[1, 'one'], [2, 'two']]);
assert.equal(map.get(1), 'one');
assert.equal(map.get(5), undefined);.set(key: K, value: V): this [ES6]
Ordnet den angegebenen Schlüssel dem angegebenen Wert zu. Wenn bereits ein Eintrag mit dem Schlüssel key vorhanden ist, wird er aktualisiert. Andernfalls wird ein neuer Eintrag erstellt. Diese Methode gibt this zurück, sodass Sie sie verketten können.
const map = new Map([[1, 'one'], [2, 'two']]);
map.set(1, 'ONE!')
.set(3, 'THREE!');
assert.deepEqual(
Array.from(map.entries()),
[[1, 'ONE!'], [2, 'two'], [3, 'THREE!']]);.has(key: K): boolean [ES6]
Gibt zurück, ob der angegebene Schlüssel in dieser Map vorhanden ist.
const map = new Map([[1, 'one'], [2, 'two']]);
assert.equal(map.has(1), true); // key exists
assert.equal(map.has(5), false); // key does not exist.delete(key: K): boolean [ES6]
Wenn ein Eintrag mit dem Schlüssel key vorhanden ist, wird er entfernt und true zurückgegeben. Andernfalls geschieht nichts und false wird zurückgegeben.
const map = new Map([[1, 'one'], [2, 'two']]);
assert.equal(map.delete(1), true);
assert.equal(map.delete(5), false); // nothing happens
assert.deepEqual(
Array.from(map.entries()),
[[2, 'two']]);Map<K,V>.prototype: Umgang mit allen Einträgenget .size: number [ES6]
Gibt zurück, wie viele Einträge diese Map hat.
const map = new Map([[1, 'one'], [2, 'two']]);
assert.equal(map.size, 2);.clear(): void [ES6]
Entfernt alle Einträge aus dieser Map.
const map = new Map([[1, 'one'], [2, 'two']]);
assert.equal(map.size, 2);
map.clear();
assert.equal(map.size, 0);Map<K,V>.prototype: Iterieren und SchleifenSowohl das Iterieren als auch das Schleifen erfolgen in der Reihenfolge, in der Einträge zu einer Map hinzugefügt wurden.
.entries(): Iterable<[K,V]> [ES6]
Gibt ein Iterable mit einem [Schlüssel, Wert]-Paar für jeden Eintrag in dieser Map zurück. Die Paare sind Arrays der Länge 2.
const map = new Map([[1, 'one'], [2, 'two']]);
for (const entry of map.entries()) {
console.log(entry);
}
// Output:
// [1, 'one']
// [2, 'two'].forEach(callback: (value: V, key: K, theMap: Map<K,V>) => void, thisArg?: any): void [ES6]
Der erste Parameter ist ein Callback, der einmal für jeden Eintrag in dieser Map aufgerufen wird. Wenn thisArg angegeben ist, wird this für jede Invokation darauf gesetzt. Andernfalls wird this auf undefined gesetzt.
const map = new Map([[1, 'one'], [2, 'two']]);
map.forEach((value, key) => console.log(value, key));
// Output:
// 'one', 1
// 'two', 2.keys(): Iterable<K> [ES6]
Gibt ein Iterable über alle Schlüssel in dieser Map zurück.
const map = new Map([[1, 'one'], [2, 'two']]);
for (const key of map.keys()) {
console.log(key);
}
// Output:
// 1
// 2.values(): Iterable<V> [ES6]
Gibt ein Iterable über alle Werte in dieser Map zurück.
const map = new Map([[1, 'one'], [2, 'two']]);
for (const value of map.values()) {
console.log(value);
}
// Output:
// 'one'
// 'two'[Symbol.iterator](): Iterable<[K,V]> [ES6]
Die Standardmethode zur Iteration über Maps. Identisch mit .entries().
const map = new Map([[1, 'one'], [2, 'two']]);
for (const [key, value] of map) {
console.log(key, value);
}
// Output:
// 1, 'one'
// 2, 'two'Wenn Sie eine wörterbuchähnliche Datenstruktur mit Schlüsseln benötigen, die weder Strings noch Symbole sind, haben Sie keine Wahl: Sie müssen eine Map verwenden.
Wenn Ihre Schlüssel jedoch entweder Strings oder Symbole sind, müssen Sie entscheiden, ob Sie ein Objekt verwenden möchten oder nicht. Eine grobe allgemeine Richtlinie lautet:
Gibt es eine feste Menge von Schlüsseln (zur Entwicklungszeit bekannt)?
Verwenden Sie dann ein Objekt obj und greifen Sie über feste Schlüssel auf die Werte zu
const value = obj.key;Kann sich die Menge der Schlüssel zur Laufzeit ändern?
Verwenden Sie dann eine Map map und greifen Sie über in Variablen gespeicherte Schlüssel auf die Werte zu
const theKey = 123;
map.get(theKey);Normalerweise möchten Sie, dass Map-Schlüssel nach Wert verglichen werden (zwei Schlüssel gelten als gleich, wenn sie den gleichen Inhalt haben). Das schließt Objekte aus. Es gibt jedoch einen Anwendungsfall für Objekte als Schlüssel: das externe Anhängen von Daten an Objekte. Aber dieser Anwendungsfall wird von WeakMaps besser bedient, bei denen Einträge Schlüssel nicht daran hindern, garbage collected zu werden (für Details siehe das nächste Kapitel).
Prinzipiell sind Maps ungeordnet. Der Hauptgrund für die Ordnung von Einträgen ist, dass Operationen, die Einträge, Schlüssel oder Werte auflisten, deterministisch sind. Das hilft zum Beispiel beim Testen.
.size, während Arrays eine .length haben?In JavaScript haben indexierbare Sequenzen (wie Arrays und Strings) eine .length, während nicht-indexierte Sammlungen (wie Maps und Sets) eine .size haben.
.length basiert auf Indizes; es ist immer der höchste Index plus eins..size zählt die Anzahl der Elemente in einer Sammlung. Quiz
Siehe Quiz-App.