Map i Set

Map i Set to dwie struktury danych, które są czymś pomiędzy tablicami i klasycznymi obiektami.

Map()

Mapy służą do tworzenia zbiorów z parami [klucz - wartość]. Przypominają one klasyczne obiekty (czy np. mapy z SASS), natomiast główną różnicą odróżniającą je od klasycznych obiektów, jest to, że kluczami może być tutaj dowolny typ danych.

Aby stworzyć mapę możemy skorzystać z jednej z 2 konstrukcji:

const map = new Map();
map.set("kolor1", "red");
map.set("kolor2", "blue");

//lub

const map = new Map([
    ["kolor1", "red"],
    ["kolor2", "blue"],
]);

Dla każdej mapy mamy dostęp do kilku metod:

set(key, value)
Ustawia nowy klucz z daną wartością

get(key)

Zwraca wartość danego klucza

has(key)

Sprawdza czy mapa ma dany klucz

delete(key)

Usuwa dany klucz i zwraca true/false jeżeli operacja się udała

clear()

Usuwa wszystkie elementy z mapy

entries()

Zwraca iterator zawierający tablicę par [klucz-wartość]

keys()

Zwraca iterator zawierający listę kluczy z danej mapy

values()

Zwraca iterator zawierający listę wartości z danej mapy

forEach

robi pętlę po elementach mapy

prototype[@@iterator]()

Zwraca iterator zawierający tablicę par [klucz-wartość]

Aby pobrać długość mapy, użyjemy właściwości size:

Klucze w mapie

Mapy w przeciwieństwie do obiektów mogą mieć klucze dowolnego typu, gdzie w przypadku obiektów (w tym tablic) są one konwertowane na tekst:

W przypadku klasycznych obiektów, klucze zawsze są konwertowane na tekst (obiekty na zapis [object Object]:

Pętla po mapie

Jeżeli będziemy chcieli iterować po mapie, możemy wykorzystać pętlę for of i poniższe funkcje zwracające iteratory:

entries()
Zwraca tablicę par klucz-wartość

keys()

Zwraca tablicę kluczy

values()

Zwraca tablicę wartości

Do iterowania możemy też wykorzystać wbudowaną w mapy funkcję forEach:

Set()

Obiekt Set jest kolekcją składającą się z unikalnych wartości, gdzie każda wartość może być zarówno typu prostego jak i złożonego. W przeciwieństwie do mapy jest to zbiór pojedynczych wartości.

Żeby stworzyć Set możemy użyć jednej z 2 składni:

Obiekty Set mają podobne właściwości i metody co obiekty typu Map, z małymi różnicami:

add(value)
Dodaje nową unikatową wartość. Zwraca Set

clear()

Usuwa wszystkie elementy ze zbioru

delete(key)

Usuwa dany klucz i zwraca true/false jeżeli operacja się udała

entries()

Zwraca iterator zawierający tablicę par [klucz-wartość]

has(key)

Sprawdza czy mapa ma dany klucz

keys()

Zwraca iterator zawierający listę kluczy z danej mapy

values()

Zwraca iterator zawierający listę wartości z danej mapy

forEach

robi pętlę po elementach mapy

prototype[@@iterator]()

Zwraca iterator zawierający tablicę par [klucz-wartość]

W przypadku Set() klucze i wartości są takie same, dlatego robiąc pętle nie ważne czy użyjemy powyższych values(), keys(), entries() czy po prostu zrobimy pętlę for of:

Set i tablice

Dzięki temu, że Set zawiera niepowtarzające się wartości, możemy to wykorzystać do odsiewania duplikatów w praktycznie dowolnym elemencie iteracyjnym - np. w tablicy:

To samo tyczy się oczywiście dynamicznie tworzonych setów:

WeakMap()

WeakMap to odmiana Mapy, którą od Map rozróżniają trzy rzeczy:

  • Nie można po niej iterować (w przyszłości będzie można, bo już zapowiedziano odpowiednie zmiany)

  • Kluczami mogą być tylko obiekty

  • Jej elementy są automatycznie usuwane gdy do danego obiektu (klucza) nie będzie referencji

Aby stworzyć nową WeakMap, skorzystamy z instrukcji:

Każda mapa daje nam kilka metod:

set(key, value)
Ustawia wartość dla klucza

get(key)

Pobiera wartość klucza

has(key)

Zwraca true/false w zależności czy dana WeakMap posiada klucz o danej nazwie

delete(key)

Usuwa wartość przypisaną do klucza

W odróżnieniu do Map elementy WeakMap są automatycznie usuwane jeżeli do danego obiektu/klucza nie będzie żadnych referencji.

Co to oznacza? W rozdziale o Garbage Collection dyskutowaliśmy o zarządzaniu pamięcią i tym, że jeżeli na dany obiekt wskazuje jakakolwiek referencja, nie może on być usunięty z pamięci. Spójrzmy jeszcze raz na tamten przykład:

Jak widzisz, mimo, że zmienna ob została ustawiona na null, obiekt nie został usunięty z pamięci, ponieważ wciąż wskazuje na niego pierwszy indeks tablicy tab[0]. Podobna sytuacja będzie w przypadku Map:

Co zresztą nie jest dziwne. W przypadku WeakMap taki element zostanie automatycznie z niej usunięty:

Dzięki czemu obiekt będzie mógł być usunięty z pamięci, bo nie będzie na niego wskazywać żadna referencja.

Uwaga. Powyższe console.log pokaże nam w debuggerze WeakMap z elementem, którego nie powinno być. Wynika to z tego, że przy czyszczeniu pamięci działają pewne mechanizmy optymalizacyjne, które sprawiają, że nie musi on być odpalany przy każdej linijce kodu. Stąd w debuggerze nie zobaczysz wyniku usunięcia nieużytecznych obiektów.

Żeby realnie sprawdzić działanie takiego kodu, trzeba by wymusić odpalenie w danym momencie Garbage Collectora. Można to zrobić na kilka sposobówarrow-up-right.

Gdzie to może się przydać? W zasadzie wszędzie, gdzie będziemy przeprowadzać dodatkowe operacje na obiektach, które potencjalnie mogą być zaraz usunięte. Wyobraźmy sobie dla przykładu, że w jednym ze skryptów mamy naście obiektów reprezentujących pliki.

Chcielibyśmy teraz napisać funkcję, która będzie do zmiennej readCount zbierać informacje o liczbie przeczytań danego pliku:

Wynik w zmiennej readCount wyszedł nam jak należy.

Problem z powyższym kodem jest ten sam co opisywany wcześniej. Na takich plikach mogą być przeprowadzane różne operacje. Może jakiś fragment kodu usunie dany plik? W takiej sytuacji nie powinniśmy już trzymać jego danych.

Niestety w przypadku Map (ale i tablic czy obiektów) referencje będą trzymane do czasu, aż ich ręcznie z nich nie usuniemy. A to oznacza, że do powyższego kodu powinniśmy dorobić funkcjonalność, która usuwała by dane o nieistniejącym już pliku. Powiedzmy, że nie wiemy gdzie, albo nie możemy modyfikować fragmentu, który usuwa dane pliki. Jak sprawić by powyższy zbiór readCount automatycznie się aktualizował?

I tutaj właśnie pojawia się zaleta WeakMap, w przypadku których odpowiednie wpisy będą automatycznie usuwane, gdy dany obiekt zostanie gdzieś usunięty i nie będzie już referencji do niego:

WeakSet()

Podobnie jak dla Map istnieją WeakMap, tak dla Setów istnieją WeakSet. Są to kolekcje składające się z unikalnych obiektów. Podobnie do WeakMap obiekty takie będą automatycznie usuwane z WeakSet, jeżeli do danego obiektu zostaną usunięte wszystkie referencje.

Każdy WeakSet udostępnia nam metody:

add(ob)
Dodaje dany obiekt do kolekcji

delete(ob)

Usuwa dany obiekt z kolekcji

has(ob)

Zwraca true/false w zależności, czy dana kolekcja zawiera dany obiekt

WeakSet idealnie nadaje się do zbierania w jeden zbiór obiektów, które potencjalnie w dalszej części skryptu mogą zostać usunięte, a więc nie powinny być trzymane w naszej "liście":

Last updated