← Назад к вопросам

Удалите дубликаты из массива

1.8 Middle🔥 131 комментариев
#Node.js и JavaScript#Алгоритмы и структуры данных

Условие

Напишите функцию removeDuplicates(arr), которая удаляет дубликаты из массива. Реализуйте несколько способов:

function removeDuplicatesSet(arr) {
  // С помощью Set
}

function removeDuplicatesFilter(arr) {
  // С помощью filter
}

function removeDuplicatesReduce(arr) {
  // С помощью reduce
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesSet(arr)); // [1, 2, 3, 4, 5]

Дополнительно

Как удалить дубликаты объектов по определенному полю?

Что проверяется

  • Знание разных подходов
  • Работа с Set
  • Методы массивов

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение: Удаление дубликатов из массива

1. С помощью Set (оптимально)

function removeDuplicatesSet<T>(arr: T[]): T[] {
  return Array.from(new Set(arr));
  // или
  // return [...new Set(arr)];
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesSet(arr)); // [1, 2, 3, 4, 5]

Плюсы: O(n) время, O(n) память, сохраняет порядок, очень быстро

Минусы: Сравнивает по ===, проблема с объектами и NaN

2. С помощью filter и indexOf

function removeDuplicatesFilter<T>(arr: T[]): T[] {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesFilter(arr)); // [1, 2, 3, 4, 5]

Плюсы: Простой, читаемый код, работает с любыми типами

Минусы: O(n²) время, медленнее на больших массивах

3. С помощью reduce

function removeDuplicatesReduce<T>(arr: T[]): T[] {
  return arr.reduce((acc: T[], item: T) => {
    if (!acc.includes(item)) {
      acc.push(item);
    }
    return acc;
  }, []);
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesReduce(arr)); // [1, 2, 3, 4, 5]

Плюсы: Функциональный стиль, контролируемый процесс

Минусы: O(n²) из-за includes, медленнее

4. С помощью Object/Map (O(n))

function removeDuplicatesMap<T>(arr: T[]): T[] {
  const seen = new Map<T, boolean>();
  return arr.filter(item => {
    if (seen.has(item)) {
      return false;
    }
    seen.set(item, true);
    return true;
  });
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesMap(arr)); // [1, 2, 3, 4, 5]

Плюсы: O(n) время, хороший контроль над логикой

Минусы: Чуть более громоздкий код

5. Удаление дубликатов объектов по полю

Способ 1: По уникальному ID

interface User {
  id: number;
  name: string;
  email: string;
}

function removeDuplicatesByKey<T>(arr: T[], key: keyof T): T[] {
  const seen = new Set();
  return arr.filter(item => {
    const value = item[key];
    if (seen.has(value)) {
      return false;
    }
    seen.add(value);
    return true;
  });
}

const users: User[] = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' },
  { id: 1, name: 'Alice 2', email: 'alice2@example.com' }, // дубликат по id
  { id: 3, name: 'Carol', email: 'carol@example.com' }
];

const unique = removeDuplicatesByKey(users, 'id');
// [
//   { id: 1, name: 'Alice', email: 'alice@example.com' },
//   { id: 2, name: 'Bob', email: 'bob@example.com' },
//   { id: 3, name: 'Carol', email: 'carol@example.com' }
// ]

Способ 2: По кастомной функции

function removeDuplicatesByFn<T>(
  arr: T[],
  fn: (item: T) => any
): T[] {
  const seen = new Set();
  return arr.filter(item => {
    const key = fn(item);
    if (seen.has(key)) {
      return false;
    }
    seen.add(key);
    return true;
  });
}

// По email
const uniqueByEmail = removeDuplicatesByFn(
  users,
  user => user.email.toLowerCase()
);

// По комбинации полей
const uniqueByCombo = removeDuplicatesByFn(
  users,
  user => `${user.id}-${user.email}`
);

Способ 3: С использованием Map для максимума/минимума

// Оставляем последний элемент с дубликатом
function removeDuplicatesKeepLast<T>(arr: T[], key: keyof T): T[] {
  const map = new Map<any, T>();
  arr.forEach(item => {
    map.set(item[key], item); // перезаписываем, остаётся последний
  });
  return Array.from(map.values());
}

// Оставляем первый элемент
function removeDuplicatesKeepFirst<T>(arr: T[], key: keyof T): T[] {
  const map = new Map<any, T>();
  arr.forEach(item => {
    const k = item[key];
    if (!map.has(k)) {
      map.set(k, item); // устанавливаем только если нет
    }
  });
  return Array.from(map.values());
}

6. Обработка специальных случаев

NaN

// Set обрабатывает NaN корректно
const arrWithNaN = [1, NaN, 2, NaN, 3];
console.log([...new Set(arrWithNaN)]); // [1, NaN, 2, 3] ✓

// filter + indexOf НЕ работает (NaN !== NaN)
console.log(arrWithNaN.filter((v, i) => arrWithNaN.indexOf(v) === i));
// [1, NaN, 2, NaN, 3] ✗ NaN не удалён

// Решение: кастомная функция
const uniqueWithNaN = arr.filter((v, i, a) => {
  if (Number.isNaN(v)) {
    return !a.slice(0, i).some(Number.isNaN);
  }
  return a.indexOf(v) === i;
});

Строки (case-insensitive)

function removeDuplicatesCaseInsensitive(arr: string[]): string[] {
  const seen = new Set();
  return arr.filter(item => {
    const lower = item.toLowerCase();
    if (seen.has(lower)) {
      return false;
    }
    seen.add(lower);
    return true;
  });
}

const words = ['hello', 'HELLO', 'Hello', 'world'];
console.log(removeDuplicatesCaseInsensitive(words)); // ['hello', 'world']

7. Сравнение производительности

const largeArr = Array(100000).fill(0).map(() => Math.floor(Math.random() * 10000));

console.time('Set');
const r1 = [...new Set(largeArr)];
console.timeEnd('Set'); // ~5ms

console.time('filter + indexOf');
const r2 = largeArr.filter((v, i, a) => a.indexOf(v) === i);
console.timeEnd('filter + indexOf'); // ~5000ms ❌

console.time('Map');
const seen = new Map();
const r3 = largeArr.filter(v => {
  if (seen.has(v)) return false;
  seen.set(v, true);
  return true;
});
console.timeEnd('Map'); // ~3ms

8. Рекомендации

СценарийМетодПричина
Примитивы, простой случайSetO(n), встроенный, быстро
Объекты по IDSet + keyO(n), простой API
Объекты по кастом функцииMap + filterO(n), гибкий
Учить собеседованиеВсе вариантыПоказывает знания
Production кодSet или MapПроизводительность

9. Тестирование

test('removeDuplicatesSet', () => {
  expect(removeDuplicatesSet([1, 2, 2, 3, 4])).toEqual([1, 2, 3, 4]);
});

test('removeDuplicatesFilter', () => {
  expect(removeDuplicatesFilter([1, 2, 2, 3, 4])).toEqual([1, 2, 3, 4]);
});

test('removeDuplicatesByKey', () => {
  const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 1, name: 'Alice2' }
  ];
  const result = removeDuplicatesByKey(users, 'id');
  expect(result).toHaveLength(2);
});

test('removeDuplicatesWithNaN', () => {
  const arr = [1, NaN, 2, NaN, 3];
  const result = [...new Set(arr)];
  expect(result).toHaveLength(4);
});