Коллекция Set
В уроке про Map гардероб хранил пары «номерок — пальто». Бывает, что пара не нужна, а важно одно: был ли уже такой гость. Представьте охранника на входе со списком гостей. Каждое имя он записывает один раз, а на вопрос «Анна уже пришла?» отвечает, заглянув в список.
В массиве такой список приходится вести вручную: перед каждым push проверять includes. Встроенная коллекция Set делает эту проверку сама. Пустую коллекцию создаёт запись new Set():
const tags = new Set();
tags.add("js");
tags.add("css");
tags.add("js");
console.log(tags.size); // 2
console.log(tags.has("css"), tags.has("html")); // true falseДавайте разберём по шагам:
new Set()создаёт пустую коллекциюtags.- Два первых
addзаписывают"js"и"css". - Третий
addс"js"ничего не меняет: такое значение уже есть. - Поэтому
sizeравен2. has("css")даётtrue, аhas("html")—false.
Что умеет Set
Set хранит отдельные значения, без ключей, и каждое значение — только один раз. Повторный вызов add с тем же значением ничего не меняет.
У Set есть:
- метод
hasотвечает, есть ли значение в коллекции; - метод
deleteудаляет значение; - свойство
sizeхранит число значений.
В отличие от includes у массива, has не перебирает значения по одному. Поэтому в большой коллекции проверка идёт быстрее. Охранник тоже не читает весь список с начала, а сразу находит нужную строку.
Такая коллекция подходит, когда важно только одно: встречалось ли значение раньше. Например, это посещённые страницы, выданные номера или теги статьи.
Какие значения равны
Set сравнивает значения так же, как оператор ===. Число 3 и строка "3" для него разные, поэтому остаются оба. Отличие от === одно: NaN === NaN даёт false, а Set считает два NaN одним значением.
Объекты сравниваются по ссылке, и два объекта с одинаковыми свойствами — два разных значения:
const values = new Set([3, "3", 3, NaN, NaN]);
console.log(values.size); // 3
const points = new Set([{ x: 1 }, { x: 1 }]);
console.log(points.size); // 2Запись new Set(arr) с массивом в скобках добавляет его элементы по очереди, как если бы для каждого вызвали add. Поэтому повторы из массива в коллекцию не попадают:
- в
valuesостаются3,"3"и одинNaN, всего три значения; - в
pointsдва разных объекта, хотя свойства у них одинаковые.
Здесь аналогия со списком гостей перестаёт работать. Охранник узнал бы двух гостей с одним именем и записал бы одного. Set с объектами смотрит не на свойства, а на сам объект: две записи { x: 1 } создают два разных объекта.
Массив без повторов
Set помнит порядок добавления, и цикл for…of проходит значения в этом порядке. Синтаксис расширения превращает Set обратно в массив. Вместе new Set и синтаксис расширения дают массив без повторов. Каждое значение стоит там, где встретилось впервые, а повторы пропадают:
const nums = [3, 1, 3, 2, 1];
const seen = new Set(nums);
for (const n of seen) {
console.log(n);
}
// 3
// 1
// 2
console.log([...seen].join(" ")); // 3 1 2Исходный массив nums при этом не меняется: new Set(nums) только читает его элементы.
Этот приём часто спрашивают на собеседовании: «Как убрать повторы из массива одной строкой?» Ответ — [...new Set(nums)].
Частая ошибка
К значению Set обратились по индексу, как к элементу массива:
const seen = new Set(["a", "b"]);
console.log(seen[0]); // ✗Программа выводит undefined. У Set есть порядок, но нет индексов, и seen.length тоже даёт undefined. Set нужен, чтобы быстро проверять, есть ли в нём значение, а не чтобы брать значения по позиции. Чтобы взять значение по номеру, сначала превратите Set в массив: [...seen][0] даёт "a".
Главное
Setхранит каждое значение один раз: повторныйaddничего не меняет.hasпроверяет, есть ли значение, аsizeхранит число значений.- Значения сравниваются как через
===, только дваNaNсчитаются одним значением, а объекты — по ссылке. [...new Set(nums)]даёт массив без повторов в порядке первого появления.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.