Коллекция Map
Чему вы научитесь:
- хранить пары «ключ — значение» в
Mapи считать в нём слова; - собирать значения без повторов в
Set; - превращать объект в строку JSON и обратно и знать, что при этом теряется;
- объяснять на собеседовании, чем
Mapотличается от обычного объекта.

Представьте гардероб в театре. Вы отдаёте пальто, а гардеробщик выдаёт номерок. Номерок — это ключ, пальто — значение. После спектакля вы показываете номерок и получаете своё пальто обратно.
Объект в JavaScript тоже хранит пары «ключ — значение», но у него есть особенность: число в роли ключа он превращает в строку. Это как гардеробщик, который читает любой номерок как текст. Для него номерок 1 и номерок "1" — один и тот же крючок. Давайте посмотрим:
const names = {};
names[1] = "one";
names["1"] = "uno";
console.log(Object.keys(names).length); // 1
const map = new Map();
map.set(1, "one");
map.set("1", "uno");
console.log(map.size); // 2
console.log(map.get(1), map.get("1")); // one unoРазберём по шагам:
names[1] = "one"записывает в объект ключ"1": число стало строкой.names["1"] = "uno"пишет под тот же ключ и заменяет"one".- Поэтому у объекта один ключ, и программа печатает
1. - Во второй половине
Mapхранит1и"1"как два разных ключа:sizeравен2.
Что такое Map
Map — встроенная коллекция пар «ключ — значение». Коллекцией называют значение, которое хранит внутри много других значений. Ключ в Map остаётся таким, каким его передали: числом, строкой, true или объектом.
Пустую коллекцию создаёт запись new Map(), и слово new в ней обязательно. new создаёт новое значение: здесь — новую пустую коллекцию. Без него вызов Map() бросает TypeError.
Методы Map
С Map работают через методы, а не через запись map.key или map[key], как у объекта:
map.set(key, value)записывает значение под ключом, а для существующего ключа заменяет старое значение;map.get(key)возвращает значение илиundefined, если такого ключа нет;map.has(key)возвращаетtrueилиfalse;map.delete(key)удаляет пару;map.sizeхранит число пар — это свойство, поэтому скобок после него нет.
Свойства length у Map нет, и map.length даёт undefined.
const stock = new Map();
stock.set("apple", 3);
stock.set("pear", 5);
stock.set("apple", 4);
stock.delete("pear");
console.log(stock.get("apple"), stock.get("plum")); // 4 undefined
console.log(stock.has("pear"), stock.size); // false 1
const ann = { name: "Ann" };
const visits = new Map();
visits.set(ann, 2);
console.log(visits.get(ann), visits.get({ name: "Ann" })); // 2 undefinedДавайте разберём склад stock:
- Первые два
setкладут яблоки и груши. - Третий
setс ключом"apple"не добавляет пару, а заменяет3на4. delete("pear")убирает груши.- Поэтому
get("apple")даёт4, аget("plum")—undefined: слив на складе нет. has("pear")даётfalse, аsizeравен1.
Ключ-объект
Ключ-объект сравнивается по ссылке. get находит значение только по тому же самому объекту. Другой объект с такими же свойствами — уже другой ключ, поэтому visits.get({ name: "Ann" }) даёт undefined.
Здесь аналогия с гардеробом перестаёт работать. Настоящий гардеробщик смотрит на число на номерке, и копия с тем же числом тоже подойдёт. Map с ключом-объектом узнаёт только тот самый номерок, а не его копию.
Подсчёт через Map
Частая задача — посчитать, сколько раз встретилось каждое слово. Для этого get сочетают с оператором ??: counts.set(word, (counts.get(word) ?? 0) + 1). Если ключа ещё нет, get вернёт undefined, ?? заменит его на 0, и под ключом запишется 1.
Нажимайте «Следующий шаг» и следите за old: для нового слова он равен 0, а для знакомого — прежнему счёту.
1const words = ["cat", "dog", "cat"];2const counts = new Map();3 4for (const word of words) {5 const old = counts.get(word) ?? 0;6 counts.set(word, old + 1);7}8 9console.log(counts.get("cat"));10console.log(counts.get("dog"));11console.log(counts.size);
Коллекция counts создана, и пар в ней пока нет.
Под каждым словом лежит его счёт, а size равен числу разных слов.
Обход Map
Map помнит порядок, в котором добавляли ключи. Цикл for…of проходит пары именно в этом порядке. Каждая пара — массив [key, value], и его удобно разобрать деструктуризацией:
const scores = new Map();
scores.set("Bob", 7);
scores.set("Ann", 9);
scores.set(10, 1);
for (const [key, value] of scores) {
console.log(key, value);
}
// Bob 7
// Ann 9
// 10 1Ключ 10 остался последним, хотя в объекте целочисленный ключ вышел бы вперёд. Запись [...scores] с синтаксисом расширения превращает Map в массив таких пар.
Map или объект
Этот вопрос часто задают на собеседовании: «Чем Map отличается от объекта?» Ответ из трёх частей:
- ключом
Mapможет быть любое значение, а объект превращает число в строку; Mapпомнит порядок добавления ключей и хранит число пар вsize;- с
Mapработают черезget,set,hasиdelete.
Объект удобен для данных с заранее известными свойствами, например user.name и user.age. Map удобен для словаря, ключи которого появляются во время работы программы: слова из текста, номера заказов, объекты.
Частая ошибка
Значение записали в Map квадратными скобками, как в объект:
const stock = new Map();
stock["apple"] = 3; // ✗
console.log(stock.get("apple"), stock.size); // undefined 0Ошибки нет, но пара в коллекцию не попала. Скобки создали у объекта stock обычное свойство, а get и size его не видят. Записывайте пары только через set: stock.set("apple", 3).
Главное
Mapхранит пары «ключ — значение», и ключ остаётся таким, каким его передали:1и"1"— разные ключи.- Пары пишут через
set, читают черезget, а их число хранитsize. - Ключ-объект ищется по ссылке: подойдёт только тот же самый объект.
- Для подсчёта пишут
counts.set(word, (counts.get(word) ?? 0) + 1).
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.