Bisher haben wir die folgenden komplexen Datenstrukturen kennengelernt:
Objekte werden zum Speichern verschlüsselter Sammlungen verwendet.
Arrays werden zum Speichern geordneter Sammlungen verwendet.
Für das wirkliche Leben reicht das aber nicht. Deshalb gibt es auch Map
und Set
.
Map ist eine Sammlung verschlüsselter Datenelemente, genau wie ein Object
. Der Hauptunterschied besteht jedoch darin, dass Map
Schlüssel aller Art zulässt.
Methoden und Eigenschaften sind:
new Map()
– erstellt die Karte.
map.set(key, value)
– speichert den Wert anhand des Schlüssels.
map.get(key)
– gibt den Wert des Schlüssels zurück, undefined
, wenn key
nicht in der Karte vorhanden ist.
map.has(key)
– gibt true
zurück, wenn der key
existiert, andernfalls false
.
map.delete(key)
– entfernt das Element (das Schlüssel/Wert-Paar) durch den Schlüssel.
map.clear()
– entfernt alles von der Karte.
map.size
– gibt die aktuelle Elementanzahl zurück.
Zum Beispiel:
let map = new Map(); map.set('1', 'str1'); // ein String-Schlüssel map.set(1, 'num1'); // eine numerische Taste map.set(true, 'bool1'); // ein boolescher Schlüssel // erinnerst du dich an das reguläre Objekt? Es würde Schlüssel in Zeichenfolgen konvertieren // Map behält den Typ bei, daher sind diese beiden unterschiedlich: alarm( map.get(1) ); // 'num1' alarm( map.get('1') ); // 'str1' alarm(map.size); // 3
Wie wir sehen, werden Schlüssel im Gegensatz zu Objekten nicht in Zeichenfolgen umgewandelt. Jede Art von Schlüssel ist möglich.
map[key]
ist nicht die richtige Art, eine Map
zu verwenden
Obwohl map[key]
auch funktioniert, z. B. können wir map[key] = 2
setzen, behandelt dies map
als einfaches JavaScript-Objekt und impliziert daher alle entsprechenden Einschränkungen (nur Zeichenfolgen-/Symbolschlüssel usw.).
Wir sollten also map
verwenden: set
, get
und so weiter.
Map kann auch Objekte als Schlüssel verwenden.
Zum Beispiel:
let john = { name: "John" }; // Speichern wir für jeden Benutzer die Anzahl seiner Besuche let visitsCountMap = new Map(); // John ist der Schlüssel für die Karte visitsCountMap.set(john, 123); alarm( visitsCountMap.get(john) ); // 123
Die Verwendung von Objekten als Schlüssel ist eine der bemerkenswertesten und wichtigsten Map
. Das Gleiche gilt nicht für Object
. String als Schlüssel in Object
ist in Ordnung, aber wir können kein anderes Object
als Schlüssel in Object
verwenden.
Versuchen wir es:
let john = { name: "John" }; let ben = { name: "Ben" }; let visitsCountObj = {}; // versuche ein Objekt zu verwenden visitsCountObj[ben] = 234; // versuche, das Ben-Objekt als Schlüssel zu verwenden visitsCountObj[john] = 123; // Versuchen Sie, das John-Objekt als Schlüssel zu verwenden. Das Ben-Objekt wird ersetzt // Das wurde geschrieben! alarm( visitsCountObj["[object Object]"] ); // 123
Da es sich bei visitsCountObj
um ein Objekt handelt, werden alle Object
, wie oben john
und ben
, in die gleiche Zeichenfolge "[object Object]"
konvertiert. Definitiv nicht das, was wir wollen.
Wie Map
Schlüssel vergleicht
Um Schlüssel auf Äquivalenz zu testen, verwendet Map
den Algorithmus SameValueZero. Es ist ungefähr dasselbe wie die strikte Gleichheit ===
, aber der Unterschied besteht darin, dass NaN
als gleich NaN
betrachtet wird. NaN
kann also auch als Schlüssel verwendet werden.
Dieser Algorithmus kann nicht geändert oder angepasst werden.
Verkettung
Jeder Aufruf map.set
gibt die Karte selbst zurück, sodass wir die Aufrufe „verketten“ können:
map.set('1', 'str1') .set(1, 'num1') .set(true, 'bool1');
Für das Durchlaufen einer map
gibt es drei Methoden:
map.keys()
– gibt eine Iterable für Schlüssel zurück,
map.values()
– gibt eine Iterable für Werte zurück,
map.entries()
– gibt eine Iterable für Einträge [key, value]
zurück, sie wird standardmäßig in for..of
verwendet.
Zum Beispiel:
let RecipeMap = new Map([ ['Gurke', 500], ['Tomaten', 350], ['Zwiebel', 50] ]); // über Schlüssel (Gemüse) iterieren for (let Gemüse von RecipeMap.keys()) { Warnung (Gemüse); // Gurke, Tomaten, Zwiebel } // über Werte (Beträge) iterieren for (let Menge von RecipeMap.values()) { Alert(Betrag); // 500, 350, 50 } // über [Schlüssel, Wert]-Einträge iterieren for (let Eintrag von RecipeMap) { // das Gleiche wie von RecipeMap.entries() Warnung(Eintrag); // Gurke,500 (und so weiter) }
Es wird die Einfügungsreihenfolge verwendet
Die Iteration erfolgt in derselben Reihenfolge, in der die Werte eingefügt wurden. Map
behält diese Reihenfolge im Gegensatz zu einem regulären Object
bei.
Darüber hinaus verfügt Map
über eine integrierte forEach
-Methode, ähnlich wie Array
:
// führt die Funktion für jedes (Schlüssel-Wert-)Paar aus RecipeMap.forEach( (Wert, Schlüssel, Karte) => { alarm(`${key}: ${value}`); // Gurke: 500 usw });
Wenn eine Map
erstellt wird, können wir ein Array (oder ein anderes iterierbares Array) mit Schlüssel/Wert-Paaren zur Initialisierung übergeben, etwa so:
// Array von [Schlüssel, Wert]-Paaren let map = new Map([ ['1', 'str1'], [1, 'num1'], [true, 'bool1'] ]); alarm( map.get('1') ); // str1
Wenn wir ein einfaches Objekt haben und daraus eine Map
erstellen möchten, können wir die integrierte Methode Object.entries(obj) verwenden, die ein Array von Schlüssel/Wert-Paaren für ein Objekt genau in diesem Format zurückgibt.
So können wir aus einem Objekt wie diesem eine Karte erstellen:
sei obj = { Name: „John“, Alter: 30 }; let map = new Map(Object.entries(obj)); alarm( map.get('name') ); // John
Hier gibt Object.entries
das Array von Schlüssel/Wert-Paaren zurück: [ ["name","John"], ["age", 30] ]
. Das ist es, was Map
braucht.
Wir haben gerade gesehen, wie man mit Object.entries(obj)
Map
aus einem einfachen Objekt erstellt.
Es gibt die Object.fromEntries
-Methode, die das Gegenteil bewirkt: Bei einem gegebenen Array von [key, value]
-Paaren erstellt sie daraus ein Objekt:
letprices = Object.fromEntries([ ['Banane', 1], ['orange', 2], ['Fleisch', 4] ]); // jetzt Preise = { Banane: 1, Orange: 2, Fleisch: 4 } alarm(prices.orange); // 2
Wir können Object.fromEntries
verwenden, um ein einfaches Objekt aus Map
abzurufen.
Beispielsweise speichern wir die Daten in einer Map
, müssen sie aber an einen Code eines Drittanbieters übergeben, der ein einfaches Objekt erwartet.
Auf geht's:
let map = new Map(); map.set('Banane', 1); map.set('orange', 2); map.set('fleisch', 4); let obj = Object.fromEntries(map.entries()); // ein einfaches Objekt erstellen (*) // Erledigt! // obj = { Banane: 1, Orange: 2, Fleisch: 4 } alarm(obj.orange); // 2
Ein Aufruf von map.entries()
gibt eine Iterable von Schlüssel/Wert-Paaren zurück, genau im richtigen Format für Object.fromEntries
.
Wir könnten die Zeile (*)
auch kürzer machen:
let obj = Object.fromEntries(map); // .entries() weglassen
Das ist das Gleiche, denn Object.fromEntries
erwartet ein iterierbares Objekt als Argument. Nicht unbedingt ein Array. Und die Standarditeration für map
gibt dieselben Schlüssel/Wert-Paare zurück wie map.entries()
. Wir erhalten also ein einfaches Objekt mit denselben Schlüsseln/Werten wie die map
.
Ein Set
ist eine spezielle Sammlungsart – „Wertemenge“ (ohne Schlüssel), bei der jeder Wert nur einmal vorkommen darf.
Seine Hauptmethoden sind:
new Set([iterable])
– erstellt die Menge und kopiert Werte daraus in die Menge, wenn ein iterable
Objekt bereitgestellt wird (normalerweise ein Array).
set.add(value)
– fügt einen Wert hinzu und gibt den Satz selbst zurück.
set.delete(value)
– entfernt den Wert, gibt true
zurück, wenn value
zum Zeitpunkt des Aufrufs existierte, andernfalls false
.
set.has(value)
– gibt true
zurück, wenn der Wert in der Menge vorhanden ist, andernfalls false
.
set.clear()
– entfernt alles aus dem Set.
set.size
– ist die Anzahl der Elemente.
Das Hauptmerkmal ist, dass wiederholte Aufrufe von set.add(value)
mit demselben Wert nichts bewirken. Aus diesem Grund kommt jeder Wert in einem Set
nur einmal vor.
Wir haben zum Beispiel Besuch und möchten uns an alle erinnern. Aber wiederholte Besuche sollten nicht zu Duplikaten führen. Ein Besucher darf nur einmal „gezählt“ werden.
Dafür ist Set
genau das Richtige:
let set = new Set(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; // Besuche, einige Benutzer kommen mehrmals set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); // set behält nur eindeutige Werte alarm( set.size ); // 3 for (Benutzer von Set lassen) { Alert(Benutzername); // John (dann Pete und Mary) }
Die Alternative zu Set
könnte ein Array von Benutzern und der Code sein, der bei jeder Einfügung mithilfe von arr.find auf Duplikate überprüft. Die Leistung wäre jedoch viel schlechter, da diese Methode das gesamte Array durchläuft und jedes Element überprüft. Set
ist intern viel besser für Eindeutigkeitsprüfungen optimiert.
Wir können eine Menge entweder mit for..of
oder mit forEach
durchlaufen:
let set = new Set(["Orangen", "Äpfel", "Bananen"]); for (let value of set) alarm(value); // das gleiche mit forEach: set.forEach((value, valueAgain, set) => { Warnung(Wert); });
Beachten Sie die lustige Sache. Die in forEach
übergebene Rückruffunktion hat drei Argumente: einen value
, dann denselben Wert , valueAgain
und dann das Zielobjekt. Tatsächlich kommt derselbe Wert in den Argumenten zweimal vor.
Dies dient der Kompatibilität mit Map
, wo der forEach
übergebene Rückruf drei Argumente hat. Sieht auf jeden Fall etwas seltsam aus. Dies kann jedoch in bestimmten Fällen hilfreich sein, um Map
durch Set
zu ersetzen, und umgekehrt.
Die gleichen Methoden, Map
für Iteratoren hat, werden ebenfalls unterstützt:
set.keys()
– gibt ein iterierbares Objekt für Werte zurück,
set.values()
– dasselbe wie set.keys()
, für Kompatibilität mit Map
,
set.entries()
– gibt ein iterierbares Objekt für Einträge [value, value]
zurück, existiert aus Kompatibilitätsgründen mit Map
.
Map
– ist eine Sammlung von Schlüsselwerten.
Methoden und Eigenschaften:
new Map([iterable])
– erstellt die Map, mit optionaler iterable
(z. B. Array) von [key,value]
-Paaren zur Initialisierung.
map.set(key, value)
– speichert den Wert anhand des Schlüssels und gibt die Karte selbst zurück.
map.get(key)
– gibt den Wert des Schlüssels zurück, undefined
, wenn key
nicht in der Karte vorhanden ist.
map.has(key)
– gibt true
zurück, wenn der key
existiert, andernfalls false
.
map.delete(key)
– entfernt das Element durch den Schlüssel, gibt true
zurück, wenn key
zum Zeitpunkt des Aufrufs existierte, andernfalls false
.
map.clear()
– entfernt alles von der Karte.
map.size
– gibt die aktuelle Elementanzahl zurück.
Die Unterschiede zu einem regulären Object
:
Alle Schlüssel, Objekte können Schlüssel sein.
Eine weitere praktische Methode ist die size
.
Set
– ist eine Sammlung eindeutiger Werte.
Methoden und Eigenschaften:
new Set([iterable])
– erstellt den Satz mit optionalen iterable
Werten (z. B. Array) für die Initialisierung.
set.add(value)
– fügt einen Wert hinzu (führt nichts aus, wenn value
vorhanden ist), gibt den Satz selbst zurück.
set.delete(value)
– entfernt den Wert, gibt true
zurück, wenn value
zum Zeitpunkt des Aufrufs existierte, andernfalls false
.
set.has(value)
– gibt true
zurück, wenn der Wert in der Menge vorhanden ist, andernfalls false
.
set.clear()
– entfernt alles aus dem Set.
set.size
– ist die Anzahl der Elemente.
Die Iteration über Map
und Set
erfolgt immer in der Einfügereihenfolge, daher können wir nicht sagen, dass diese Sammlungen ungeordnet sind, aber wir können Elemente nicht neu anordnen oder ein Element direkt anhand seiner Nummer abrufen.
Wichtigkeit: 5
Sei arr
ein Array.
Erstellen Sie eine Funktion unique(arr)
, die ein Array mit eindeutigen Elementen von arr
zurückgeben soll.
Zum Beispiel:
Funktion unique(arr) { /* Ihr Code */ } let value = ["Hare", "Krishna", "Hare", "Krishna", „Krishna“, „Krishna“, „Hare“, „Hare“, „:-O“ ]; alarm( unique(values) ); // Hase, Krishna, :-O
PS: Hier werden Zeichenfolgen verwendet, es können jedoch Werte jeglichen Typs sein.
PPS Verwenden Sie Set
um eindeutige Werte zu speichern.
Öffnen Sie eine Sandbox mit Tests.
Funktion unique(arr) { return Array.from(new Set(arr)); }
Öffnen Sie die Lösung mit Tests in einer Sandbox.
Wichtigkeit: 4
Anagramme sind Wörter, die aus der gleichen Anzahl gleicher Buchstaben, jedoch in unterschiedlicher Reihenfolge bestehen.
Zum Beispiel:
Nickerchen - Pfanne Ohr - sind - Ära Betrüger – Hektar – Lehrer
Schreiben Sie eine Funktion aclean(arr)
die ein von Anagrammen bereinigtes Array zurückgibt.
Zum Beispiel:
let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alarm( aclean(arr) ); // „nap,teachers,ear“ oder „PAN,cheaters,era“
Von jeder Anagrammgruppe sollte nur ein Wort übrig bleiben, egal welches.
Öffnen Sie eine Sandbox mit Tests.
Um alle Anagramme zu finden, teilen wir jedes Wort in Buchstaben auf und sortieren sie. Bei der Buchstabensortierung sind alle Anagramme gleich.
Zum Beispiel:
Nickerchen, Schwenk -> anp Ohr, Ära, sind -> aer Betrüger, Hektar, Lehrer -> aceehrst ...
Wir verwenden die nach Buchstaben sortierten Varianten als Zuordnungsschlüssel, um nur einen Wert pro Schlüssel zu speichern:
Funktion aclean(arr) { let map = new Map(); für (lass Wort von arr) { // Teilen Sie das Wort nach Buchstaben auf, sortieren Sie sie und fügen Sie sie wieder zusammen let sorted = word.toLowerCase().split('').sort().join(''); // (*) map.set(sortiert, Wort); } return Array.from(map.values()); } let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alarm( aclean(arr) );
Die Buchstabensortierung erfolgt durch die Aufrufkette in der Zeile (*)
.
Der Einfachheit halber teilen wir es in mehrere Zeilen auf:
let sorted = Wort // PAN .toLowerCase() // pan .split('') // ['p','a','n'] .sort() // ['a','n','p'] .verbinden(''); // anp
Zwei verschiedene Wörter 'PAN'
und 'nap'
erhalten die gleiche nach Buchstaben sortierte Form 'anp'
.
Die nächste Zeile fügt das Wort in die Karte ein:
map.set(sortiert, Wort);
Wenn wir jemals wieder auf ein Wort in derselben nach Buchstaben sortierten Form stoßen, würde es den vorherigen Wert mit demselben Schlüssel in der Karte überschreiben. Wir haben also immer maximal ein Wort pro Buchstabenform.
Am Ende nimmt Array.from(map.values())
einen iterierbaren Wert über Kartenwerte (wir benötigen keine Schlüssel im Ergebnis) und gibt ein Array davon zurück.
Hier könnten wir anstelle der Map
auch ein einfaches Objekt verwenden, da Schlüssel Zeichenfolgen sind.
So kann die Lösung aussehen:
Funktion aclean(arr) { sei obj = {}; for (let i = 0; i < arr.length; i++) { let sorted = arr[i].toLowerCase().split("").sort().join(""); obj[sortiert] = arr[i]; } return Object.values(obj); } let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alarm( aclean(arr) );
Öffnen Sie die Lösung mit Tests in einer Sandbox.
Wichtigkeit: 5
Wir möchten ein Array von map.keys()
in einer Variablen abrufen und dann arrayspezifische Methoden darauf anwenden, z. B. .push
.
Aber das funktioniert nicht:
let map = new Map(); map.set("name", "John"); letkeys = map.keys(); // Fehler: „keys.push“ ist keine Funktion keys.push("more");
Warum? Wie können wir den Code reparieren, damit keys.push
funktioniert?
Das liegt daran, dass map.keys()
ein iterierbares, aber kein Array zurückgibt.
Wir können es mit Array.from
in ein Array konvertieren:
let map = new Map(); map.set("name", "John"); letkeys = Array.from(map.keys()); keys.push("more"); alarm(keys); // Name, mehr