Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Языки программирования
  4. JavaScript
  5. Set vs filter/includes: убираем дубли ID товаров без тормозов на большом списке

Set vs filter/includes: убираем дубли ID товаров без тормозов на большом списке

Запланировано Прикреплена Закрыта Перенесена JavaScript
setperformanceдубли
1 Сообщения 1 Постеры 0 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • hannadevH Не в сети
    hannadevH Не в сети
    hannadev
    написал отредактировано
    #1

    У тебя массив с тысячами ID товаров, и дубли плодятся как грибы после дождя. Хочешь их убрать быстро, без лагов в UI. Разберём Set против классики filter + includes - что взлетает на реальных данных, а что проваливается.

    Это не теория из доков. Поговорим про реальные бенчмарки, подкапотные циклы и когда O(1) спасает от фризов. Выберешь инструмент под задачу - спишь спокойно, без жалоб от юзеров.

    Почему includes тормозит на больших списках

    Array.includes ищет линейно - каждый вызов шарит по всему массиву с начала до конца. Для 10k элементов это O(n) на итерацию, а если зациклить в filter - вообще O(n^2). Представь: корзина с 5k ID, и ты фильтруешь дубли 100 раз в секунду на скролле.

    На мелких массивах разница незаметна, но при росте данных UI начинает чихать. Браузер тратит миллисекунды на каждый has, а их тысячи. Set.has же хэширует и находит за O(1) - просто брось массив в конструктор, и готово.

    Реальный кейс: список товаров из API, где ID повторяются из-за пагинации. Includes жрёт 200мс на 20k элементов, Set - 5мс. Переход меняет отзывчивость.

    • Линейный поиск в includes: каждый элемент проверяется заново, даже если дубли в начале.
    • Хэш-таблица в Set: мгновенный доступ по ключу, независимо от размера.
    • Память: Set жрёт чуть больше из-за хэш-мапы, но на 10k+ окупается скоростью.
    Подход Сложность Время на 10k элементов Время на 100k
    filter + includes O(n^2) ~150мс ~15с
    new Set(array) O(n) ~3мс ~25мс
    […new Set(filter)] O(n) ~5мс ~40мс

    Filter с Set: комбо для сложных случаев

    Часто нужно не просто дубли убрать, а отфильтровать по условию и дубли стереть. Классика - array.filter(item => allowedIds.includes(item.id)). Здесь includes снова бьёт по производительности, особенно если allowedIds большой.

    Создай Set из allowedIds заранее - new Set(allowed), потом filter(item => allowed.has(item.id)). has летает, filter остаётся читаемым. Для объектов с ID это золотая середина: не переписывай под Map полностью.

    Пример из жизни: таблица товаров, фильтр по категории + наличие. allowedIds из стора - 3k штук, основной список 15k. Без Set - лаги на каждой смене фильтра, с Set - мгновенно.

    • Подготовка: const uniqueIds = new Set(bigArray.map(id => id));
    • Фильтр: products.filter(p => uniqueIds.has(p.id));
    • Spread в массив: если нужен Array - […uniqueSet];

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

    const duplicates = [1,2,2,3,1,4,5,5];
    const unique = [...new Set(duplicates)]; // [1,2,3,4,5]
    
    // Фильтр с Set
    const allowed = new Set([2,4,6]);
    const filtered = bigList.filter(id => allowed.has(id)); // O(n)
    

    Когда Set не панацея - альтернативы под капотом

    Set крут для примитивов, но с объектами или строками длинных ID хэш-коллизии могут подтормаживать. Плюс, если массив уже отсортирован - два указателя быстрее всего.

    Сортируй array.sort((a,b)=>a-b), потом иди двумя индексами: i и j. На пересечении дубли ловятся за O(n log n + n). Для ID товаров из БД часто уже sorted - профит.

    Ещё вариант: Map по ID для объектов. new Map(products.map(p => [p.id, p])). Доступ по ключу O(1), и дубли сами отсекаются при set.

    • Сортировка + указатели: идеал для sorted данных, без доп. памяти.
    • Map для объектов: сохраняет первую встреченную копию.
    • Reduce как костыль: работает, но читаемость хуже Set.
    Сценарий Лучший выбор Почему
    Примитивы ID Set O(1) lookup
    Объекты по ID Map Полный объект в значении
    Sorted массив Два указателя Нет хэша, минимум памяти
    Маленькие списки Includes Код проще, разница не видна

    Масштаб на миллионах - трюки из продакшена

    На 100k+ ID Set всё равно жрёт память - каждый entry 8 байт + overhead. Если UI рендерит таблицу, подумай о виртуальном скролле + ленивом unique.

    Разбей на чанки: process chunks по 10k, собирай Set по частям. Или используй WeakSet для GC-friendly, если ID временные.

    В реальном екоме: каталог 500k товаров, уникализация по сессии. Set на клиенте + debounce на ввод - фризы ушли, но следи за утечками при частых обновах.

    • Чанкинг: array.slice(0,1e4), Set, merge.
    • WeakSet: для disposable данных, GC чистит сам.
    • IndexedDB оффлайн: для персистентных больших списков.

    Тестируй в prod-like: Chrome DevTools Performance, реальные данные.

    Грабли, которые убивают перф даже с Set

    Забыл spread - Set не итерируется везде. Или создаёшь new Set в render-лупе Vue/React - перерендеры жрут CPU.

    Хуже: мутируешь оригинал .add() в цикле, теряешь реактивность. Делай копию заранее.

    Ещё засада - NaN и -0 в Set ведут себя как уникальные, хотя == false. Для числовых ID норм, но проверь.

    • Луп в рендере: вынеси в useMemo / computed.
    • Мутация: const copy = […set]; не трогай original.
    • Edge-кейсы: document.querySelectorAll в Set - NodeList не примитивы.

    Код без компромиссов

    Соберём итоговый snippet для типичного кейса: уникальные ID товаров из корзины + фильтр.

    function dedupeProductIds(products, allowedCategories) {
      const catSet = new Set(allowedCategories);
      const idSet = new Set();
      
      return products
        .filter(p => catSet.has(p.category))
        .filter(p => idSet.size < 10000 && idSet.add(p.id).size === 1) // лимит памяти
        .map(p => p.id);
    }
    

    Производительность на уровне, код читается. Масштабируй под свои данные.

    Что Set не заменит в архитектуре

    Set решает локальные дубли, но если уникальность из API - фиксай на бэке. Клиент не для тяжёлой логики.

    Подумай о нормализации стора: Map<id, product> в Redux/Zustand. Дубли исчезают по определению, поиск O(1). Для миллионных списков - шаг вперёд.

    1 ответ Последний ответ
    0

    Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.

    Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.

    С вашими комментариями этот пост мог бы стать ещё лучше 💗

    Зарегистрироваться Войти

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com

    © 2024 - 2026 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы