Включите исполнение JavaScript в браузере, чтобы запустить приложение.

Map и Set в JavaScript для удобной работы с данными

Map и Set — часто недооцененные возможности JavaScript, которые новички обходят стороной, предпочитая привычные объекты и массивы. Но у этих структур есть свои сильные стороны: они решают ряд задач проще, быстрее и чище. Вот когда стоит выбирать именно их.

Map и Object ― не просто словари

Вы наверняка используете объекты (Object) для хранения данных в формате «ключ-значение». Это основа основ в JavaScript. Например:

const user = {

  name: «Андрей»,

  age: 30,

  isAdmin: false

};

console.log(user.name); // «Андрей»
javascript

Это удобно, но у объектов есть ограничение: их ключи могут быть только строками или специальным типом Symbol. Если вы попытаетесь использовать что-то другое (например, другой объект) в качестве ключа, JavaScript неявно преобразует его в строку (часто в «[object Object]»), что может привести к неожиданным коллизиям и потере данных.

И тут на помощь приходит Map. Map — это тоже коллекция пар «ключ-значение», но с важным отличием. Ключом в Map может быть значение абсолютно любого типа – строка, число, булево значение, null, undefined, Symbol, и даже другой объект или функция.

Ключевые отличия Map от Object:

  1. Map — любой тип, Object — только string или Symbol.
  2. Map гарантированно сохраняет порядок добавления элементов и перебирает их именно в этом порядке. У обычных объектов до недавнего времени порядок не был гарантирован. Хотя современные движки часто сохраняют его для строковых ключей, полагаться на это не стоит.
  3. У Map есть простое свойство size для получения количества элементов. У объекта для этого нужно получать массив ключей (Object.keys(obj).length).
  4. Map напрямую итерируется (например, через for...of), что удобнее, чем перебор ключей объекта (Object.keys, Object.entries).
  5. Производительность ― для сценариев с частым добавлением и удалением пар ключ-значение Map часто оказывается производительнее.

Представьте Object как простой блокнот, где вы пишете имя (строка) и рядом номер телефона. А Map – это продвинутая картотека, где карточкой-ключом может быть не только имя, но и фотография человека, его отпечаток пальца (другой тип данных), а к этой карточке привязана информация (значение).

Set и массивы ― коллекции без дубликатов

Массивы (Array) – это упорядоченные коллекции элементов. Вы можете хранить в них что угодно, и элементы могут повторяться:

const userRoles = [“admin”, “editor”, “viewer”, “editor”]; // «editor» повторяется

console.log(userRoles[1]); // «editor»

console.log(userRoles.length); // 4
javascript

Массивы отлично подходят для хранения списков, где важен порядок и допустимы дубликаты. А что, если нам нужна коллекция, где каждый элемент уникален? Например, список ID пользователей, которые посетили страницу, — нам не нужно хранить дубликаты, если пользователь обновил страницу 5 раз. Для этого идеально подходит Set — это коллекция, которая хранит только уникальные значения любого типа. Если вы попытаетесь добавить в Set элемент, который там уже есть, ничего не произойдет.

Ключевые отличия Set от Array:

  1. Set гарантирует уникальность элементов. Array позволяет дубликаты.
  2. В Set проверка наличия элемента (mySet.has(element)) работает очень быстро, особенно на больших коллекциях, в отличие от myArray.includes(element) или myArray.indexOf(element) !== -1, которые могут быть медленнее.
  3. Set используется в первую очередь для хранения уникальных коллекций и быстрой проверки принадлежности к этой коллекции. Array — для упорядоченных списков и операций, связанных с индексами.
  4. У элементов Set нет числовых индексов, как у массива. Доступ к элементам происходит через итерацию или проверку наличия.

Приведем аналогию. Array — это список покупок, где «молоко» может быть упомянуто два раза, если вам нужно две упаковки. Set — что-то вроде списка приглашенных на вечеринку гостей. Неважно, сколько раз вы вписывали имя друга: в итоговом списке он будет только один раз.

Применение Map

Map — это коллекция ключ-значение, но ее настоящая сила раскрывается там, где обычный объект (Object) пасует. Давайте рассмотрим конкретные ситуации, где Map не просто удобен, а является наилучшим решением.

Представьте, что вы строите интерактивную веб-страницу, и вам нужно хранить дополнительную информацию, связанную с конкретными элементами на странице (DOM-узлами). Если вы попытаетесь использовать DOM-узел как ключ объекта, он будет неявно приведен к строке ([object HTMLDivElement] или что-то подобное). Если у вас несколько разных div-элементов, они все превратятся в один и тот же ключ в объекте. Данные для одного элемента будут перезаписывать данные для другого.

В свою очередь Map примет сам DOM-элемент в качестве уникального ключа.

const element1 = document.getElementById('button1');

const element2 = document.getElementById('tooltip1');

const elementDataMap = new Map();

// Используем сами DOM-элементы как ключи

elementDataMap.set(element1, { clicks: 0, text: "Нажми меня" });

elementDataMap.set(element2, { isVisible: false, position: "top" });

console.log(elementDataMap.size); // 2 - все верно!

// Получаем данные по конкретному элементу

const buttonData = elementDataMap.get(element1);

if (buttonData) {

  buttonData.clicks++;

  console.log(`Кнопка нажата ${buttonData.clicks} раз.`);

}

const tooltipData = elementDataMap.get(element2);

console.log(tooltipData); // { isVisible: false, position: 'top' }

// Проверка наличия

if (elementDataMap.has(element1)) {

    console.log("Данные для кнопки есть.");

}

// Удаление

// elementDataMap.delete(element2);
javascript

Точно так же ключами могут быть любые другие объекты (например, экземпляры ваших классов), функции, NaN. Map сохраняет их идентичность.

Узнать, сколько элементов в Map, элементарно — myMap.size. С объектом нужно вызывать Object.keys(myObj).length, что менее прямолинейно и потенциально менее эффективно.

  • Map создан для итерации. Цикл for...of напрямую работает с парами [key, value]. Также есть удобные методы .keys(), .values(), .entries(), возвращающие итераторы.
  • Производительность. Хотя для простых случаев разница может быть невелика, в сценариях, где вы постоянно добавляете и удаляете большое количество пар ключ-значение, Map может показывать лучшую производительность, чем манипуляции с ключами объекта.

Применение Set

Set приходит на помощь, когда вам нужна коллекция, но с одним важным условием: никаких дубликатов. А еще — когда нужно быстро проверить, есть ли уже такой элемент в коллекции, или нет.

У вас есть массив, полученный из разных источников, и в нем могут быть повторы. Вам нужен список уникальных значений. Массивы спокойно хранят дубликаты. Чтобы получить уникальные значения, нужно писать дополнительный код (фильтрация, использование indexOf в цикле), что может быть громоздко и не всегда эффективно. Set делает это за вас автоматически и очень элегантно.

//Массив строк (тегов)

const userInputTags = ["js", "css", "html", "js", "react", "css", "js"];

const uniqueTags = [...new Set(userInputTags)]; // Сразу создаем Set и конвертируем в массив

console.log(uniqueTags); // [ 'js', 'css', 'html', 'react' ]
javascript

Вы просто передаете массив в конструктор new Set(), и он сам позаботится об удалении дубликатов.

Представьте, что у вас есть большой список разрешенных ID пользователей, или список заблокированных IP-адресов. Вам нужно постоянно проверять, принадлежит ли входящий ID/IP к этому списку. Метод set.has(value) оптимизирован для быстрой проверки наличия. Он работает значительно быстрее, чем поиск в массиве, особенно на больших объемах данных.

// Предположим, этот список может быть очень большим

const adminUserIDs = new Set([5, 12, 42, 101, 555, 1024]);

function checkAdminAccess(userId) {

  console.time(`Проверка доступа для ${userId}`); // Замеряем время

  const isAdmin = adminUserIDs.has(userId); // Используем быстрый .has()

  console.timeEnd(`Проверка доступа для ${userId}`); // Заканчиваем замер

  if (isAdmin) {

    console.log(`Пользователь ${userId} - администратор.`);

  } else {

    console.log(`Пользователь ${userId} - обычный пользователь.`);

  }

}

checkAdminAccess(42);  // Быстро найдет

checkAdminAccess(100); // Быстро определит, что такого нет
javascript

Хотя на маленьких наборах разница незаметна, для тысяч и миллионов записей Set будет на порядки быстрее.

Отличия Map от Set

Главное отличие запомнить просто: Map хранит пары ключ ― значение. Он используется для ассоциаций, когда вам нужно связать одно значение с другим. Вы ищете и работаете со значениями по их ключам.

Set хранит только значения (уникальные). Он используется для коллекций уникальных элементов, когда вам важен сам факт наличия или отсутствия элемента в наборе.

Хотите «словарь», где ключ может быть чем угодно? Используйте Map. Хотите хранить список уникальных ID, имен, объектов без дубликатов? Используйте Set.

Map и Set — это специализированные структуры данных, которые делают код для определенных задач более читаемым, предсказуемым и часто более производительным. Не бойтесь использовать их там, где они подходят лучше, чем стандартные Object и Array.