Mapa a sada

Doposud jsme se dozvěděli o následujících komplexních datových strukturách:

  • Objekty se používají k ukládání klíčovaných kolekcí.
  • Pole se používají k ukládání objednaných kolekcí.

Ale to pro skutečný život nestačí. Proto Map a Set také existují.

Mapa

Mapa je sbírka klíčovaných datových položek, stejně jako Object . Ale hlavní rozdíl je v tom, že Map umožňuje klíče jakéhokoli typu.

Metody a vlastnosti jsou:

  • new Map() – vytvoří mapu.
  • map.set(key, value) – uloží hodnotu pomocí klíče.
  • map.get(key) – vrátí hodnotu pomocí klíče, undefined pokud key na mapě neexistuje.
  • map.has(key) – vrátí true pokud key existuje, false jinak.
  • map.delete(key) – odebere hodnotu pomocí klíče.
  • map.clear() – odstraní vše z mapy.
  • map.size – vrátí aktuální počet prvků.

Například:

let map = new Map();

map.set('1', 'str1'); // a string key
map.set(1, 'num1'); // a numeric key
map.set(true, 'bool1'); // a boolean key

// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

Jak vidíme, na rozdíl od objektů se klíče nepřevádějí na řetězce. Je možný jakýkoli typ klíče.

map[key] není správný způsob použití Map

Ačkoli map[key] funguje také např. můžeme nastavit map[key] = 2 , jedná se o ošetření map jako prostý objekt JavaScriptu, takže to znamená všechna odpovídající omezení (pouze řetězcové/symbolové klíče a tak dále).

Měli bychom tedy použít map metody:set , get a tak dále.

Mapa může také používat objekty jako klíče.

Například:

let john = { name: "John" };

// for every user, let's store their visits count
let visitsCountMap = new Map();

// john is the key for the map
visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123

Použití objektů jako klíčů je jedním z nejpozoruhodnějších a nejdůležitějších Map funkce. Totéž se nepočítá pro Object . Řetězec jako klíč v Object je v pořádku, ale nemůžeme použít jiný Object jako klíč v Object .

Zkusme to:

let john = { name: "John" };
let ben = { name: "Ben" };

let visitsCountObj = {}; // try to use an object

visitsCountObj[ben] = 234; // try to use ben object as the key
visitsCountObj[john] = 123; // try to use john object as the key, ben object will get replaced

// That's what got written!
alert( visitsCountObj["[object Object]"] ); // 123

Jako visitsCountObj je objekt, převede všechny Object klíče, například john a ben výše, na stejný řetězec "[object Object]" . Rozhodně ne to, co chceme.

Jak Map porovnává klíče

Chcete-li otestovat ekvivalenci klíčů, Map používá algoritmus SameValueZero. Je to zhruba stejné jako přísná rovnost === , ale rozdíl je v tom, že NaN je považováno za rovné NaN . Takže NaN lze použít také jako klíč.

Tento algoritmus nelze změnit ani přizpůsobit.

Řetězení

Každých map.set call vrátí samotnou mapu, takže můžeme hovory „řetězit“:

map.set('1', 'str1')
 .set(1, 'num1')
 .set(true, 'bool1');

Iterace přes mapu

Pro smyčkování přes map , existují 3 způsoby:

  • map.keys() – vrátí iterovatelnou hodnotu pro klíče,
  • map.values() – vrací iterovatelnou hodnotu pro hodnoty,
  • map.entries() – vrátí iterovatelnou hodnotu pro položky [key, value] , ve výchozím nastavení se používá v for..of .

Například:

let recipeMap = new Map([
 ['cucumber', 500],
 ['tomatoes', 350],
 ['onion', 50]
]);

// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
 alert(vegetable); // cucumber, tomatoes, onion
}

// iterate over values (amounts)
for (let amount of recipeMap.values()) {
 alert(amount); // 500, 350, 50
}

// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
 alert(entry); // cucumber,500 (and so on)
}
Použije se objednávka vložení

Iterace probíhá ve stejném pořadí, v jakém byly vloženy hodnoty. Map zachovává toto pořadí, na rozdíl od běžného Object .

Kromě toho Map má vestavěný forEach metoda, podobná Array :

// runs the function for each (key, value) pair
recipeMap.forEach( (value, key, map) => {
 alert(`${key}: ${value}`); // cucumber: 500 etc
});

Object.entries:Mapa z objektu

Když Map je vytvořen, můžeme předat pole (nebo jinou iterovatelnou) s páry klíč/hodnota pro inicializaci, takto:

// array of [key, value] pairs
let map = new Map([
 ['1', 'str1'],
 [1, 'num1'],
 [true, 'bool1']
]);

alert( map.get('1') ); // str1

Pokud máme prostý objekt a chtěli bychom vytvořit Map z něj pak můžeme použít vestavěnou metodu Object.entries(obj), která vrací pole párů klíč/hodnota pro objekt přesně v tomto formátu.

Můžeme tedy vytvořit mapu z objektu, jako je tento:

let obj = {
 name: "John",
 age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John

Zde Object.entries vrátí pole párů klíč/hodnota:[ ["name","John"], ["age", 30] ] . To je to, co Map potřeby.

Object.fromEntries:Objekt z mapy

Právě jsme viděli, jak vytvořit Map z prostého objektu s Object.entries(obj) .

Je tam Object.fromEntries metoda, která dělá opak:je dána polem [key, value] párů, vytvoří z nich objekt:

let prices = Object.fromEntries([
 ['banana', 1],
 ['orange', 2],
 ['meat', 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange); // 2

Můžeme použít Object.fromEntries získat prostý objekt z Map .

Např. data ukládáme do Map , ale musíme jej předat kódu třetí strany, který očekává prostý objekt.

Tady to je:

let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map.entries()); // make a plain object (*)

// done!
// obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2

Volání na číslo map.entries() vrátí iterovatelný pár klíč/hodnota přesně ve správném formátu pro Object.fromEntries .

Můžeme také vytvořit řádek (*) kratší:

let obj = Object.fromEntries(map); // omit .entries()

To je stejné, protože Object.fromEntries očekává iterovatelný objekt jako argument. Ne nutně pole. A standardní iterace pro map vrátí stejné páry klíč/hodnota jako map.entries() . Dostaneme tedy prostý objekt se stejnými hodnotami klíč/hodnota jako map .

Nastavit

A Set je speciální typová kolekce – „množina hodnot“ (bez klíčů), kde se každá hodnota může vyskytovat pouze jednou.

Jeho hlavní metody jsou:

  • new Set(iterable) – vytvoří sadu, a pokud iterable objekt (obvykle pole), zkopíruje z něj hodnoty do sady.
  • set.add(value) – přidá hodnotu, vrátí samotnou množinu.
  • set.delete(value) – odebere hodnotu, vrátí true pokud value existoval v okamžiku volání, jinak false .
  • set.has(value) – vrátí true pokud hodnota v sadě existuje, jinak false .
  • set.clear() – odebere vše ze sady.
  • set.size – je počet prvků.

Hlavním rysem je opakované volání set.add(value) se stejnou hodnotou nic nedělají. To je důvod, proč se každá hodnota objevuje v Set pouze jednou.

Například k nám přicházejí návštěvníci a rádi bychom si na všechny vzpomněli. Opakované návštěvy by však neměly vést k duplicitám. Návštěvník musí být „započítán“ pouze jednou.

Set je pro to to pravé:

let set = new Set();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

// visits, some users come multiple times
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// set keeps only unique values
alert( set.size ); // 3

for (let user of set) {
 alert(user.name); // John (then Pete and Mary)
}

Alternativa k Set může být pole uživatelů a kód pro kontrolu duplikátů při každém vložení pomocí arr.find. Ale výkon by byl mnohem horší, protože tato metoda prochází celým polem a kontroluje každý prvek. Set je mnohem lépe interně optimalizován pro kontroly jedinečnosti.

Iterace přes sadu

Můžeme smyčku přes sadu buď pomocí for..of nebo pomocí forEach :

let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) alert(value);

// the same with forEach:
set.forEach((value, valueAgain, set) => {
 alert(value);
});

Všimněte si té legrační věci. Funkce zpětného volání předána v forEach má 3 argumenty:a value a poté stejnou hodnotu valueAgain a poté cílový objekt. Ve skutečnosti se stejná hodnota objeví v argumentech dvakrát.

Důvodem je kompatibilita s Map kde zpětné volání prošlo forEach má tři argumenty. Vypadá to trochu divně, určitě. Může však pomoci nahradit Map s Set v určitých případech s lehkostí a naopak.

Stejné metody Map má pro iterátory jsou také podporovány:

  • set.keys() – vrací iterovatelný objekt pro hodnoty,
  • set.values() – stejně jako set.keys() , kvůli kompatibilitě s Map ,
  • set.entries() – vrací iterovatelný objekt pro položky [value, value] , existuje kvůli kompatibilitě s Map .

Shrnutí

Map – je sbírka klíčovaných hodnot.

Metody a vlastnosti:

  • new Map([iterable]) – vytvoří mapu s volitelným iterable (např. pole) [key,value] páry pro inicializaci.
  • map.set(key, value) – uloží hodnotu pomocí klíče, vrátí samotnou mapu.
  • map.get(key) – vrátí hodnotu pomocí klíče, undefined pokud key na mapě neexistuje.
  • map.has(key) – vrátí true pokud key existuje, false jinak.
  • map.delete(key) – odebere hodnotu pomocí klíče, vrátí true pokud key existoval v okamžiku hovoru, jinak false .
  • map.clear() – odstraní vše z mapy.
  • map.size – vrátí aktuální počet prvků.

Rozdíly od běžného Object :

  • Jakékoli klíče, objekty mohou být klíče.
  • Další pohodlné metody, size vlastnictví.

Set – je sbírka jedinečných hodnot.

Metody a vlastnosti:

  • new Set([iterable]) – vytvoří sadu s volitelným iterable (např. pole) hodnot pro inicializaci.
  • set.add(value) – přidá hodnotu (nedělá nic, pokud value existuje), vrátí samotnou množinu.
  • set.delete(value) – odebere hodnotu, vrátí true pokud value existoval v okamžiku volání, jinak false .
  • set.has(value) – vrátí true pokud hodnota v sadě existuje, jinak false .
  • set.clear() – odebere vše ze sady.
  • set.size – je počet prvků.

Iterace přes Map a Set je vždy v pořadí vložení, takže nemůžeme říci, že tyto kolekce jsou neuspořádané, ale nemůžeme změnit pořadí prvků nebo přímo získat prvek podle jeho čísla.


No