Перейти к содержимому
Тема 14 из 16
Теория

Map и Set

Зачем это нужно

Объекты и массивы закрывают большинство задач, но не все. Иногда нужен словарь, где ключами могут быть не только строки, или важен быстрый подсчёт количества. А иногда нужен набор без повторов: уникальные посетители, список тегов, проверка «видели ли мы это раньше». Для этого в JavaScript есть две коллекции — Map и Set.

Set — набор уникальных значений

const tags = new Set();
tags.add("js");
tags.add("css");
tags.add("js");          // повтор — игнорируется
console.log(tags.size);
console.log(tags.has("css"));
tags.delete("css");
console.log([...tags]);

Вывод:

2
true
["js"]

Set можно создать сразу из массива — повторы исчезнут:

const visits = ["Аня", "Тимур", "Аня", "Ира", "Тимур"];
const people = new Set(visits);
console.log(people.size);
console.log([...people]);

Вывод:

3
["Аня","Тимур","Ира"]

[...set] превращает набор обратно в массив. Порядок — как значения добавлялись.

Перебор Set

for (const tag of new Set(["a", "b"])) {
  console.log(tag);
}

Вывод:

a
b

Map — словарь

Map хранит пары «ключ → значение», как объект, но:

  • ключом может быть что угодно: число, объект, даже функция (у объекта ключи — только строки);
  • размер — свойство size;
  • нет «встроенных» ключей вроде constructor, которые есть у любого объекта;
  • порядок ключей всегда равен порядку добавления.
const stock = new Map();
stock.set("apple", 10);
stock.set("pear", 4);
stock.set("apple", 12);           // перезапись
console.log(stock.get("apple"));
console.log(stock.get("plum"));   // нет такого ключа
console.log(stock.has("pear"), stock.size);
stock.delete("pear");
console.log([...stock]);

Вывод:

12
undefined
true 2
[["apple",12]]

Map можно создать сразу из массива пар: new Map([["a", 1], ["b", 2]]).

Перебор Map

const prices = new Map([["tea", 50], ["coffee", 90]]);
for (const [name, price] of prices) {
  console.log(`${name}: ${price}`);
}
console.log([...prices.keys()], [...prices.values()]);

Вывод:

tea: 50
coffee: 90
["tea","coffee"] [50,90]

Каждый элемент Map при переборе — пара [ключ, значение], её удобно разбирать деструктуризацией.

Объект ↔ Map

const obj = { a: 1, b: 2 };
const map = new Map(Object.entries(obj));
const back = Object.fromEntries(map);
console.log(map.get("b"), back);

Вывод:

2 {"a":1,"b":2}

Что выбрать?

Задача Чем решать
Описание одной вещи (пользователь, товар) объект {}
Список по порядку массив []
Подсчёт, словарь с произвольными ключами, частые добавления/удаления Map
Уникальность, быстрая проверка «есть ли» Set

Как это работает внутри

Set и Map устроены как хеш-таблица: по значению ключа сразу вычисляется место, где его искать. Поэтому set.has(x) работает мгновенно даже для миллиона элементов. А array.includes(x) перебирает массив с начала — для большого массива и многих проверок это в тысячи раз медленнее. Если в цикле проверяешь «есть ли элемент в другом массиве» — сначала сделай из того массива Set.

Уникальность в Set проверяется как === (кроме NaN, который считается равным себе). Поэтому два разных объекта { a: 1 } в Set оба останутся — это разные ссылки.

Частые ошибки

  1. Квадратные скобки для Map. map["a"] = 1 создаёт обычное свойство, а не элемент Map: map.get("a") вернёт undefined. Используй set и get.
  2. length вместо size. У Map и Set — size.
  3. Ждать, что Set уберёт одинаковые объекты. new Set([{a: 1}, {a: 1}]).size → 2.
  4. Забыли превратить в массив. У Set нет map и filter — сначала [...set].
  5. includes в цикле по большим массивам. Медленно. Сделай Set и используй has.

Шпаргалка

Set Map
new Set(arr) new Map([[k, v], …])
s.add(x) m.set(k, v)
s.has(x) m.has(k), m.get(k)
s.delete(x) m.delete(k)
s.size m.size
[...s] — в массив [...m] — массив пар
for (const x of s) for (const [k, v] of m)

Без повторов

Лёгкая+10 XP

Напиши функцию unique(items), которая возвращает новый массив без повторяющихся значений, в порядке первого появления.

unique([1, 2, 2, 3, 1])     → [1, 2, 3]
unique(["a", "b", "a"])     → ["a", "b"]

Подключи коннектор, чтобы решать задачи

Код задач выполняется на твоём компьютере — так быстрее и надёжнее, чем в браузере. Для этого нужна бесплатная программа-коннектор CodeWithMe. Подключить её нужно один раз.

Ищем коннектор на этом компьютере…

  1. 1
    Скачай коннектор для своей системы (Windows, macOS или Linux).Скачать коннекторНа странице загрузки выбери файл со своей системой в названии, например windows-amd64.
  2. 2Запусти его и выбери папку, где будут лежать твои решения. Python и Node.js он поставит туда сам, когда понадобится.
  3. 3
    Введи ниже код из 6 цифр — он написан в окне коннектора («Код для сайта CodeWithMe»). Если браузер спросит про доступ к локальной сети — разреши.

Коннектор открытый (исходный код на GitHub), работает только на твоём компьютере и выполняет код лишь после ввода кода сопряжения.