> Как удалить дубликаты из массива чисел и строк без использования Set в JavaScript? (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Tilda

Стек: JavaScript

> Пример ответа

Короткий ответ

Удалить дубликаты можно через filter с indexOf, reduce с аккумулятором-массивом, или через объект-хэш (Map). Для чисел и строк подходит хэш-подход: проходим по массиву, сохраняем уникальные ключи в объекте и возвращаем только те элементы, которые встретились впервые. Это O(n) по времени и O(n) по памяти.

Подробное объяснение

Основная задача - сохранить порядок элементов и эффективно проверять уникальность. Set даёт O(1) на проверку, но без него нужно реализовать аналогичную структуру. Подходы различаются по производительности:

  • filter + indexOf: O(n²), так как indexOf проходит по массиву каждый раз. Подходит только для маленьких массивов.
  • reduce + includes: тоже O(n²), includes - линейный поиск.
  • Объект-хэш: O(n). Используем свойства объекта (или Map) как хэш-таблицу. Ключом может быть сам элемент (для строк и чисел это безопасно). Проверяем, есть ли ключ, если нет - добавляем в результат и устанавливаем ключ.
  • Map: предпочтительнее объекта, так как не наследует прототипные ключи и сохраняет тип ключа (число vs строка "1").

Важно: для смешанных типов (числа и строки) объект может привести к коллизиям, если число и строка с одинаковым значением (например, 1 и "1") считаются разными. В таком случае используем Map или храним ключи с префиксом типа.

На практике

В реальных проектах Set - стандартное решение. Но если задача стоит без Set, выбираем хэш-подход с Map для надёжности. Для массивов до 1000 элементов можно использовать filter + indexOf, но это медленнее. Важно учитывать, что NaN и undefined требуют особой обработки: indexOf не найдёт NaN, а объект-хэш может их потерять.

Пример кода

JAVASCRIPT
// Хэш-подход с Map (рекомендуемый)
function removeDuplicates(arr) {
const seen = new Map();
const result = [];
for (const item of arr) {
if (!seen.has(item)) {
seen.set(item, true);
result.push(item);
}
}
return result;
}
// filter + indexOf (O(n²), для маленьких массивов)
function removeDuplicatesSimple(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
// reduce + includes (тоже O(n²))
function removeDuplicatesReduce(arr) {
return arr.reduce((acc, item) => {
if (!acc.includes(item)) acc.push(item);
return acc;
}, []);
}
// Пример с учётом типов (число 1 и строка "1" - разные)
function removeDuplicatesTyped(arr) {
const seen = new Map();
return arr.filter(item => {
const key = typeof item + ':' + item;
if (seen.has(key)) return false;
seen.set(key, true);
return true;
});
}

Как отвечать на собеседовании

Начни с краткого ответа, затем объясни trade-off между подходами. Упомяни, что filter + indexOf - это квадратичная сложность, а хэш-подход - линейная. Подчеркни, что для смешанных типов нужен Map или префикс типа. Если спросят про NaN, скажи, что indexOf не работает, а хэш-подход с Map корректно обрабатывает NaN (Map использует SameValueZero). Покажи пример кода с Map как наиболее универсальный.

Что проверяет интервьюер

  • Понимание алгоритмической сложности (O(n) vs O(n²)).
  • Умение реализовать хэш-таблицу вручную.
  • Знание особенностей типов в JavaScript (число vs строка, NaN).
  • Способность выбирать оптимальное решение под контекст.

Типичные ошибки

  • Использование объекта без проверки hasOwnProperty (может вернуть ложные срабатывания на прототипные ключи, например, toString).
  • Игнорирование различий между 1 и "1" - объект приводит ключи к строке, поэтому 1 и "1" считаются одинаковыми.
  • Забывают про NaN: indexOf не находит NaN, а объект-хэш может его потерять.
  • Мутация исходного массива вместо возврата нового.
  • Использование delete на объекте-хэше в цикле - это ломает производительность.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью