Avatar ·

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

Всем привет! Я новичок в JavaScript и столкнулся с проблемой при работе с массивами. У меня есть массив, который содержит повторяющиеся значения, и мне нужно оставить только уникальные элементы. Например, у меня есть такой массив: const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'kiwi', 'orange']; Я хочу получить на выходе: ['apple', 'banana', 'orange', 'kiwi'] (порядок не важен, главное — удалить дубликаты). Я пробовал использовать простой цикл for и проверять, есть ли уже элемент в новом массиве, но это кажется слишком громоздким. Может быть, есть более элегантное решение? Я слышал про Set, но не совсем понимаю, как его применить. Также меня интересует, какой способ считается самым быстрым для больших массивов (например, 10 000+ элементов). И ещё: можно ли это сделать без создания нового массива, а изменить исходный? Заранее спасибо за помощь! <<>>

Использование Set — самый простой и современный способ

Объект Set в JavaScript автоматически хранит только уникальные значения. Чтобы получить массив уникальных элементов, просто преобразуйте Set обратно в массив.

const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'kiwi', 'orange'];
const uniqueFruits = [...new Set(fruits)];
console.log(uniqueFruits); // ['apple', 'banana', 'orange', 'kiwi']

Объяснение:

  • new Set(fruits) создаёт коллекцию, где каждый элемент встречается только один раз.
  • Оператор ... (spread) разворачивает Set в элементы нового массива.
  • Этот способ работает для примитивных типов (строки, числа, булевы значения, null, undefined).
  • Порядок элементов сохраняется в порядке первого появления.

Обработка объектов и смешанных типов

Если массив содержит объекты или смешанные типы, Set сравнивает по ссылке, а не по содержимому. Для глубокого сравнения используйте JSON.stringify или библиотеки типа Lodash:

const arr = [{id: 1}, {id: 1}, {id: 2}];
const unique = [...new Set(arr.map(item => JSON.stringify(item)))].map(str => JSON.parse(str));
console.log(unique); // [{id: 1}, {id: 2}]

Внимание: этот метод не работает, если объекты содержат функции или циклические ссылки.

Альтернативные методы (для старых браузеров или специфических задач)

Метод filter + indexOf:

const unique = fruits.filter((item, index) => fruits.indexOf(item) === index);

Этот способ медленнее на больших массивах, так как indexOf проходит по массиву каждый раз.

Метод reduce:

const unique = fruits.reduce((acc, item) => {
  if (!acc.includes(item)) acc.push(item);
  return acc;
}, []);

Работает, но тоже не оптимален для больших данных.

Производительность для больших массивов

Для массивов с 10 000+ элементов Set значительно быстрее (O(n) против O(n²) у filter + indexOf). Вот пример замеров:

// Создаём большой массив с дубликатами
const bigArray = Array.from({ length: 10000 }, (_, i) => i % 1000);

console.time('Set');
const uniqueSet = [...new Set(bigArray)];
console.timeEnd('Set'); // ~0.1ms

console.time('Filter');
const uniqueFilter = bigArray.filter((item, index) => bigArray.indexOf(item) === index);
console.timeEnd('Filter'); // ~50ms (в 500 раз медленнее!)

Модификация исходного массива

Хотя обычно рекомендуется создавать новый массив, вы можете изменить исходный:

const arr = [1, 2, 1, 3, 2];
arr.splice(0, arr.length, ...new Set(arr));
console.log(arr); // [1, 2, 3]

Итог: для 99% случаев используйте [...new Set(array)] — это чисто, быстро и читаемо.

<<>>

Использование Object (для совместимости со старыми браузерами)

Если вам нужно поддерживать очень старые браузеры (IE11 и ниже), где Set может отсутствовать, можно использовать объект в качестве хеш-таблицы:

function getUnique(arr) {
  const seen = {};
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (!seen[item]) {
      seen[item] = true;
      result.push(item);
    }
  }
  return result;
}

const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'kiwi', 'orange'];
console.log(getUnique(fruits)); // ['apple', 'banana', 'orange', 'kiwi']

Этот метод:

  • Работает за O(n) — быстрее, чем filter + indexOf.
  • Не требует дополнительных полифиллов.
  • Но есть нюанс: ключи объекта преобразуются в строки, поэтому числа и строки могут смешиваться (например, 1 и '1' будут считаться одинаковыми).

Для чисел и строк этот способ надёжен. Для объектов используйте Map вместо объекта — он сохраняет тип ключа:

function getUniqueWithMap(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;
}

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