`Map` Obiekt

Ten artykuł wyjaśnia obiekt Map.

Wyjaśnimy krok po kroku, od podstawowych operacji do praktycznych przykładów przydatnych w rzeczywistych sytuacjach.

YouTube Video

Map Obiekt

Map to kolekcja przechowująca pary klucz-wartość. Jest podobny do obiektu, ale różni się tym, że jako klucze można używać dowolnego typu, takiego jak obiekty, funkcje czy prymitywy, a kolejność wstawiania jest zachowywana.

Podstawy Map

Najpierw zobaczmy, jak utworzyć Map i wykonać podstawowe operacje.

Poniższy kod to minimalny przykład tworzący pustą mapę, dodający klucze i pobierający wartości.

1// Create an empty Map and add key-value pairs
2const m = new Map();
3m.set('a', 1);
4m.set('b', 2);
5
6console.log(m.get('a')); // 1
7console.log(m.size);     // 2
  • W tym kodzie możesz dodawać elementy za pomocą set, pobierać wartości za pomocą get oraz sprawdzać liczbę elementów dzięki właściwości size.
  • Map zachowuje kolejność wstawiania, dzięki czemu nadaje się do operacji zależnych od tej kolejności.

Zachowanie metod set, get, has i delete

Oto przykłady typowych operacji odczytu, zapisu, sprawdzania istnienia i usuwania.

Za pomocą poniższego kodu można sprawdzić zwracane wartości i efekty każdej z metod.

 1// Demonstrate set, get, has, and delete
 2const m2 = new Map();
 3m2.set('x', 10);
 4console.log(m2.has('x'));  // true
 5console.log(m2.get('y'));  // undefined
 6
 7m2.delete('x');
 8console.log(m2.has('x'));  // false
 9
10// set returns the map itself, allowing method chaining
11m2.set('a', 1).set('b', 2);
12console.log(m2);  // Map { 'a' => 1, 'b' => 2 }
  • get zwraca undefined, jeśli podany klucz nie istnieje. has sprawdza istnienie klucza, a delete usuwa klucz.
  • Ponadto set zwraca samą mapę, co umożliwia łączenie wywołań metod (chainowanie).

Każdy typ może być użyty jako klucz (używanie obiektów jako kluczy)

Jedną z głównych zalet Map jest to, że można bezpośrednio używać obiektów jako kluczy.

Poniższy przykład pokazuje, jak powiązać wartości w Mapie za pomocą obiektów jako kluczy.

 1// Use objects as keys in a Map
 2const keyObj = { id: 1 };
 3const keyFunc = () => {};
 4const objMap = new Map();
 5
 6// Another object with the same content but a different reference
 7const anotherKeyObj = { id: 1 };
 8
 9objMap.set(keyObj, 'objectValue');
10objMap.set(keyFunc, 'functionValue');
11objMap.set(anotherKeyObj, 'anotherValue');
12
13console.log(objMap.get(keyObj));         // 'objectValue'
14console.log(objMap.get(keyFunc));        // 'functionValue'
15console.log(objMap.get(anotherKeyObj));  // 'anotherValue'
  • Przy używaniu obiektów jako kluczy ważne jest, aby była to ta sama referencja. Nawet jeśli ich zawartość jest identyczna, obiekty o różnych referencjach są traktowane jako różne i nie są tym samym kluczem.

Iteracja (pętla)

Map zachowuje kolejność wstawiania, dlatego często używa się iteracji.

Poniżej pokazujemy, jak korzystać z for...of, forEach oraz metod keys(), values() i entries().

 1// Iterating a Map with for...of and forEach
 2const iterMap = new Map([['a', 1], ['b', 2], ['c', 3]]);
 3
 4// for...of over entries (default)
 5for (const [key, value] of iterMap) {
 6  console.log(key, value);
 7}
 8
 9// forEach callback
10iterMap.forEach((value, key) => {
11  console.log(key, value);
12});
13
14// keys() and values()
15console.log([...iterMap.keys()]);   // ['a','b','c']
16console.log([...iterMap.values()]); // [1,2,3]
  • entries() zwraca tablicę par [klucz, wartość], którą można zamienić na tablicę używając składni rozproszenia (spread). Warto zauważyć, że funkcja zwrotna forEach otrzymuje argumenty w kolejności value, key.

Konwersja między Map a Object

Możesz przekonwertować istniejący obiekt na Mapę lub zamienić Mapę na zwykły obiekt albo tablicę.

 1// Convert between Map and Object / Array
 2const obj = { a: 1, b: 2 };
 3const mapFromObj = new Map(Object.entries(obj)); // Object -> Map
 4console.log(mapFromObj.get('a')); // 1
 5
 6const objFromMap = Object.fromEntries(mapFromObj); // Map -> Object
 7console.log(objFromMap); // { a: 1, b: 2 }
 8
 9const arrayFromMap = [...mapFromObj]; // Map -> Array of [key, value]
10console.log(arrayFromMap); // [['a',1], ['b',2]]
  • Użycie Object.entries i Object.fromEntries ułatwia konwersję. Jednak, ponieważ klucze obiektów mogą być tylko łańcuchami znaków (stringami) lub symbolami, klucze nie będące stringami zostaną utracone podczas konwersji z powrotem do obiektu.

Praktyczny wzorzec: Zliczanie częstotliwości (Count Map)

Podczas zliczania częstości występowania elementów w tablicy użycie Map upraszcza ten proces.

Poniższy kod to przykład wykorzystania Mapy do zliczania częstotliwości występowania łańcuchów w tablicy i ich sortowania.

1// Count frequencies with Map
2const arr = ['apple','banana','apple','orange','banana','apple'];
3const freq = new Map();
4
5for (const item of arr) {
6  freq.set(item, (freq.get(item) || 0) + 1);
7}
8
9console.log([...freq.entries()]); // [['apple',3], ['banana',2], ['orange',1]]
  • Użycie Map umożliwia łatwe sprawdzanie istnienia oraz aktualizację. Można to zrobić także za pomocą obiektów, lecz Map jest bardziej intuicyjne przy pracy z dowolnymi kluczami.

Uwagi dotyczące Map i JSON.stringify (serializacja)

JSON.stringify nie potrafi bezpośrednio serializować Mapy. Jeśli potrzebujesz zapisać Mapę, musisz ją najpierw przekonwertować.

Poniższy przykład pokazuje, jak zamienić Mapę na tablicę przed serializacją do JSON oraz jak ją potem przywrócić.

1// Serialize and deserialize a Map
2const m3 = new Map([['x', 1], ['y', 2]]);
3const json = JSON.stringify([...m3]); // convert to array first
4console.log(json); // '[["x",1],["y",2]]'
5
6const restored = new Map(JSON.parse(json));
7console.log(restored.get('x')); // 1
  • Mapy, które muszą być zapisane lub przesłane, należy przed serializacją przekonwertować na tablice. Podczas przywracania użyj JSON.parse do zamiany na tablicę, a następnie przekształć ją ponownie w Mapę.

Wprowadzenie do WeakMap i jak jej używać

WeakMap różni się tym, że jej klucze są przechowywane jako słabe referencje (mogą zostać usunięte przez garbage collector).

Jest przydatna do przechowywania cache lub metadanych powiązanych z obiektami, które są kluczami, automatycznie zwalniając je, gdy kluczowy obiekt zostanie usunięty.

1// WeakMap for metadata tied to object lifecycle
2const wm = new WeakMap();
3let obj = {};
4wm.set(obj, { meta: 'info' });
5console.log(wm.get(obj)); // { meta: 'info' }
6
7obj = null; // now the object can be GC'd and its entry removed from WeakMap
  • WeakMap nie można enumerować ani sprawdzić jej rozmiaru, ale jest przydatna do zapobiegania wyciekom pamięci.

Podsumowanie

Map to wygodna kolekcja, która w przeciwieństwie do obiektów pozwala na dowolny typ jako klucz i zachowuje kolejność wstawiania. Poznając zarówno podstawowe operacje, jak i zaawansowane zastosowania, możesz zarządzać danymi bardziej elastycznie i intuicyjnie. Używając odpowiednio Object oraz Map zależnie od przypadku, możesz znacznie poprawić przejrzystość i czytelność kodu.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video