Как удалить дубликаты из массива и получить только уникальные значения в JavaScript?
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'kiwi', 'orange'];
Я хочу получить на выходе: ['apple', 'banana', 'orange', 'kiwi'] — то есть каждый фрукт встречается только один раз, а порядок сохраняется по первому вхождению.
Я пробовал написать свою функцию с циклом и проверкой через indexOf, но код получился громоздким и не очень читаемым. Мне кажется, что в современном JavaScript должны быть более элегантные способы сделать это.
Подскажите, пожалуйста, какие существуют методы для удаления дубликатов из массива? Какие из них считаются лучшими по производительности и читаемости? Интересуют решения как для массивов примитивов (строки, числа), так и для массивов объектов, если это возможно. Заранее спасибо за помощь!
<<1. Использование Set (самый простой и быстрый способ)
Set — это встроенная структура данных в JavaScript, которая автоматически хранит только уникальные значения. Если передать массив в конструктор Set, дубликаты будут удалены. Затем мы преобразуем Set обратно в массив с помощью оператора spread (...) или Array.from().
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 в элементы нового массива.
Преимущества:
- Максимально лаконичный код (одна строка).
- Высокая производительность (O(n) — линейная сложность).
- Работает с любыми примитивами: числами, строками, символами.
Важно: Set не удаляет дубликаты объектов, так как объекты сравниваются по ссылке, а не по содержимому. Для объектов потребуется другой подход.
2. Использование filter() + indexOf() (классический метод)
Этот способ проверяет, является ли текущий элемент первым вхождением в массиве. Если indexOf(element) равен текущему индексу, значит элемент встречается впервые.
const numbers = [1, 2, 3, 2, 4, 3, 5];
const uniqueNumbers = numbers.filter((value, index, self) => {
return self.indexOf(value) === index;
});
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
Как это работает:
filterпроходит по каждому элементу.indexOfвозвращает индекс первого вхождения значения.- Если он совпадает с текущим индексом — элемент уникален, и мы его сохраняем.
Недостаток: indexOf внутри цикла делает метод O(n²) по времени, что медленно для больших массивов (более 10 000 элементов).
3. Использование reduce() + аккумулятор (гибкий способ)
Этот метод позволяет контролировать процесс и легко адаптируется под разные типы данных.
const items = ['a', 'b', 'a', 'c', 'b'];
const uniqueItems = items.reduce((acc, item) => {
if (!acc.includes(item)) {
acc.push(item);
}
return acc;
}, []);
console.log(uniqueItems); // ['a', 'b', 'c']
Плюсы: Читаемый код, легко модифицируется (например, можно добавить проверку на глубокое сравнение объектов).
Минусы: includes тоже имеет сложность O(n), поэтому общая сложность O(n²).
4. Для массивов объектов — с помощью Map или Set с ключом
Объекты нельзя сравнивать напрямую через Set. Но можно использовать Map или Set с сериализацией (например, по свойству id):
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' }, // дубликат
{ id: 3, name: 'Charlie' }
];
// Удаляем дубликаты по id
const uniqueUsers = [...new Map(users.map(item => [item.id, item])).values()];
console.log(uniqueUsers);
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' }]
Объяснение:
users.map(item => [item.id, item])создает массив пар [ключ, значение].new Map(...)автоматически оставляет только последнюю запись с уникальным ключом..values()извлекает объекты, а[...]преобразует их в массив.
Рекомендация
Для большинства случаев используйте Set — это современный, быстрый и читаемый способ. Для массивов объектов с уникальным идентификатором — комбинацию Map + map. Избегайте filter + indexOf на больших массивах из-за низкой производительности.
Альтернативный подход: использование библиотеки Lodash
Если вы работаете над проектом, где уже используется библиотека Lodash, вы можете воспользоваться её функцией _.uniq(). Для массивов объектов есть _.uniqBy(), которая позволяет указать критерий уникальности (например, поле объекта).
// Для примитивов
const numbers = [1, 2, 3, 2, 4, 3];
const unique = _.uniq(numbers); // [1, 2, 3, 4]
// Для объектов по полю id
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' }
];
const uniqueUsers = _.uniqBy(users, 'id');
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
Плюсы:
- Простота и читаемость (одна функция).
- Поддержка сложных случаев (глубокое сравнение, сравнение по нескольким полям).
- Хорошо протестирована и оптимизирована.
Минусы:
- Дополнительная зависимость в проекте (размер библиотеки ~24 КБ в сжатом виде).
- Излишне, если нужно только удалить дубликаты — нативный Set справляется лучше.
Когда использовать: Если вы уже используете Lodash в проекте или вам нужно удалять дубликаты из массива объектов по сложному критерию (например, по комбинации полей).