Set (Множество)

Set — коллекция уникальных значений без дубликатов. Каждое значение может присутствовать только один раз. Проверка наличия элемента — O(1).

Зачем нужно

Set решает задачу уникальности за O(1): удаление дубликатов, проверка «видели ли мы это значение», операции над множествами (объединение, пересечение, разность). Без Set эти задачи требуют O(n) на каждую проверку.

Где используется

  • Удаление дубликатов из массива
  • Проверка уникальности (уникальные посетители, уникальные слова)
  • Теги, категории, фильтры
  • Математические операции над множествами
  • Отслеживание посещённых узлов в BFS/DFS

Предпосылки

Set в JavaScript

// Создание
const set = new Set();
const fromArray = new Set([1, 2, 3, 2, 1]); // {1, 2, 3}
const fromString = new Set('hello');          // {'h', 'e', 'l', 'o'}

// Основные операции
set.add(1);         // добавить          O(1)
set.add(2);
set.add(1);         // дубликат — игнорируется
set.has(1);         // true              O(1)
set.has(3);         // false             O(1)
set.delete(1);      // true              O(1)
set.size;           // 1
set.clear();        // очистить

// Итерация (порядок вставки сохраняется)
const colors = new Set(['red', 'green', 'blue']);
for (const color of colors) {
  console.log(color); // red, green, blue
}

colors.forEach(color => console.log(color));
const arr = [...colors]; // ['red', 'green', 'blue']

Удаление дубликатов

// Самый простой способ удалить дубликаты
const numbers = [1, 2, 3, 2, 4, 1, 5, 3];
const unique = [...new Set(numbers)]; // [1, 2, 3, 4, 5]

// Уникальные объекты (Set сравнивает по ссылке!)
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 1, name: 'Alice' }, // другой объект, та же data
];

// Set НЕ уберёт дубликаты объектов (разные ссылки)
const uniqueUsers = [...new Set(users)]; // всё ещё 3 элемента

// Решение: уникальность по ключу
function uniqueBy(array, key) {
  const seen = new Set();
  return array.filter(item => {
    const value = item[key];
    if (seen.has(value)) return false;
    seen.add(value);
    return true;
  });
}

console.log(uniqueBy(users, 'id'));
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]

Операции над множествами

const A = new Set([1, 2, 3, 4]);
const B = new Set([3, 4, 5, 6]);

// Объединение (Union): A ∪ B
const union = new Set([...A, ...B]);
// {1, 2, 3, 4, 5, 6}

// Пересечение (Intersection): A ∩ B
const intersection = new Set([...A].filter(x => B.has(x)));
// {3, 4}

// Разность (Difference): A \ B
const difference = new Set([...A].filter(x => !B.has(x)));
// {1, 2}

// Симметрическая разность: (A \ B) ∪ (B \ A)
const symmetricDiff = new Set(
  [...A].filter(x => !B.has(x)).concat([...B].filter(x => !A.has(x)))
);
// {1, 2, 5, 6}

// Подмножество: A ⊆ B
function isSubset(setA, setB) {
  return [...setA].every(x => setB.has(x));
}

// ES2025+ — встроенные методы
// A.union(B), A.intersection(B), A.difference(B),
// A.symmetricDifference(B), A.isSubsetOf(B), A.isSupersetOf(B)

WeakSet

WeakSet хранит только объекты и не препятствует сборке мусора.

const weakSet = new WeakSet();

let obj = { data: 'important' };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true

obj = null; // объект может быть собран GC
// weakSet больше не содержит ссылку

// Не поддерживает: size, итерацию, clear
// Используется для: отслеживание объектов без утечек памяти

Практические примеры

// Подсчёт уникальных посетителей
const visitors = new Set();

function trackVisitor(userId) {
  visitors.add(userId);
}

trackVisitor('user_1');
trackVisitor('user_2');
trackVisitor('user_1'); // дубликат
console.log(visitors.size); // 2

// Проверка наличия дубликатов в массиве — O(n)
function hasDuplicates(array) {
  return new Set(array).size !== array.length;
}

// Первый неповторяющийся символ
function firstUnique(str) {
  const count = {};
  for (const ch of str) {
    count[ch] = (count[ch] || 0) + 1;
  }
  for (const ch of str) {
    if (count[ch] === 1) return ch;
  }
  return null;
}

Сложность операций

Операция Сложность
add(value) O(1)
has(value) O(1)
delete(value) O(1)
size O(1)
Итерация O(n)

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

  1. Думать, что Set дедуплицирует объекты по содержимому. Set сравнивает по ссылке (===)
  2. Забывать, что Set сохраняет порядок вставки. Это гарантировано спецификацией
  3. Использовать массив вместо Set для проверок includes. arr.includes — O(n), set.has — O(1)
  4. Не знать о WeakSet. Для отслеживания объектов без утечек памяти

Практика

  1. Реализуйте функции union, intersection, difference для Set
  2. Напишите findCommonElements(arr1, arr2, arr3) — элементы, которые есть во всех трёх массивах
  3. Реализуйте longestConsecutiveSequence([100, 4, 200, 1, 3, 2]) → 4 (последовательность 1,2,3,4)

Связанные темы

  • Map (ключ-значение, а не просто значение)
  • Hash Table (Set реализован на хэш-таблице)
  • Array (альтернатива)
  • Graph (visited set для BFS/DFS)

Ресурсы

  • MDN — Set
  • learn.javascript.ru — Set и WeakSet
  • TC39 — Set methods proposal (union, intersection и др.)