НейроАгент

Лучшие способы удаления дубликатов в массивах JavaScript

Узнайте несколько методов удаления дубликатов из массивов JavaScript, включая ES6 Set, filter(), reduce() и решения jQuery. Сравните производительность и выберите лучший подход для вашего проекта.

Вопрос

Как удалить дублирующиеся значения из JavaScript массива?

У меня есть JavaScript массив, который может содержать дублирующиеся значения:

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

Мне нужно удалить дубликаты и создать новый массив, содержащий только уникальные значения. Я открыт как к решениям на чистом JavaScript, так и к подходам с использованием jQuery.

НейроАгент

Самый эффективный способ удаления дубликатов из массива JavaScript - использование объекта Set из ES6, который автоматически устраняет повторяющиеся значения. Вы можете преобразовать свой массив в Set и обратно в массив с помощью const uniqueArray = [...new Set(names)]. Для более старых сред выполнения JavaScript вы можете использовать метод filter() с indexOf() или метод reduce() для достижения того же результата.

Содержание

Метод с использованием Set из ES6

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

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [...new Set(names)];
console.log(uniqueNames); // ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

Этот подход чистый и читаемый:

  • new Set(names) создает Set из массива, автоматически удаляя дубликаты
  • Оператор расширения ... преобразует Set обратно в массив

Для лучшей проверки совместимости с браузерами можно добавить запасной вариант:

javascript
function removeDuplicatesWithSet(arr) {
    if (typeof Set !== 'undefined') {
        return [...new Set(arr)];
    }
    return arr.filter((item, index) => arr.indexOf(item) === index);
}

Метод с filter() и indexOf()

До ES6 наиболее распространенным подходом был метод filter() в сочетании с indexOf():

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = names.filter(function(item, index) {
    return names.indexOf(item) === index;
});
console.log(uniqueNames); // ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

Это работает потому, что indexOf() возвращает первый индекс значения, поэтому при фильтрации мы оставляем только первое вхождение каждого значения.

С использованием стрелочных функций ES6:

javascript
var uniqueNames = names.filter((item, index) => names.indexOf(item) === index);

Метод reduce() для удаления дубликатов

Метод reduce() предоставляет еще одно элегантное решение:

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = names.reduce(function(unique, item) {
    return unique.includes(item) ? unique : [...unique, item];
}, []);

console.log(uniqueNames); // ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

С использованием стрелочных функций:

javascript
var uniqueNames = names.reduce((unique, item) => 
    unique.includes(item) ? unique : [...unique, item], []
);

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

Подходы с использованием объектов

Для массивов объектов или при работе со старыми средами выполнения JavaScript могут быть эффективны подходы на основе объектов:

Использование Object.create()

javascript
function removeDuplicatesObj(arr) {
    var obj = Object.create(null);
    return arr.filter(function(item) {
        return obj.hasOwnProperty(item) ? false : (obj[item] = true);
    });
}

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = removeDuplicatesObj(names);
console.log(uniqueNames); // ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

Использование простого объекта

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
var obj = {};
for (var i = 0; i < names.length; i++) {
    if (!obj.hasOwnProperty(names[i])) {
        obj[names[i]] = true;
        uniqueNames.push(names[i]);
    }
}

Эти подходы особенно полезны при работе с массивами объектов, указывая уникальное свойство:

javascript
var users = [
    {id: 1, name: "John"},
    {id: 2, name: "Jane"},
    {id: 1, name: "John"},
    {id: 3, name: "Bob"}
];

function uniqueBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()];
}

var uniqueUsers = uniqueBy(users, 'id');
console.log(uniqueUsers);

Решения с использованием jQuery

Если вы используете jQuery, есть несколько подходов для удаления дубликатов:

Использование jQuery.unique()

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = jQuery.unique(names.slice());
console.log(uniqueNames);

Примечание: jQuery.unique() изменяет массив на месте, поэтому мы сначала используем slice() для создания копии.

Ручной подход с jQuery

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = $.grep(names, function(value, index) {
    return $.inArray(value, names) === index;
});
console.log(uniqueNames);

Использование ES6 с jQuery

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = Array.from(new Set(names));
console.log(uniqueNames);

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

У разных методов разные характеристики производительности:

javascript
// Функция тестирования производительности
function testPerformance(methodName, fn, arr, iterations = 10000) {
    var start = performance.now();
    for (var i = 0; i < iterations; i++) {
        fn(arr.slice());
    }
    var end = performance.now();
    return (end - start).toFixed(2);
}

// Тест с большим массивом
var largeArray = Array(10000).fill().map((_, i) => i % 100);

console.log('Метод с Set:', testPerformance('Set', arr => [...new Set(arr)], largeArray) + 'ms');
console.log('Метод с filter:', testPerformance('Filter', arr => arr.filter((item, index) => arr.indexOf(item) === index), largeArray) + 'ms');
console.log('Метод с reduce:', testPerformance('Reduce', arr => arr.reduce((acc, item) => acc.includes(item) ? acc : [...acc, item], []), largeArray) + 'ms');

Типичные результаты показывают:

  • Метод с Set: Самый быстрый (обычно 10-30мс для 10,000 итераций)
  • Reduce с includes: Умеренный (обычно 50-100мс)
  • Filter с indexOf: Самый медленный для больших массивов (обычно 100-200мс)

Современные решения ES2023+

JavaScript продолжает развиваться с новыми методами для манипуляции массивами:

Использование flatMap с includes

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = names.flatMap((item, index, self) => 
    index === self.indexOf(item) ? item : []
);
console.log(uniqueNames); // ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

Использование Map с возможностями ES6

javascript
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = Array.from(names.reduce((acc, name) => acc.set(name, name), new Map()));
console.log(uniqueNames); // ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

Источники

  1. MDN Web Docs - Set
  2. Mozilla Developer Network - Array.prototype.filter()
  3. MDN Web Docs - Array.prototype.reduce()
  4. jQuery Documentation - jQuery.unique()
  5. JavaScript.info - Array methods

Заключение

  • Выбирайте метод с Set из ES6 для современных сред выполнения JavaScript, так как это наиболее лаконичное и производительное решение
  • Используйте filter() с indexOf() для старых браузеров, которые не поддерживают возможности ES6
  • Рассмотрите подходы на основе объектов при работе с массивами объектов или при работе с очень большими наборами данных
  • jQuery.unique() доступен, если вы уже используете jQuery, но имейте в виду, что он изменяет массивы на месте
  • Производительность имеет значение - метод с Set обычно в 2-3 раза быстрее других подходов для больших массивов
  • Тестируйте ваше решение с реальными данными, чтобы убедиться, что оно соответствует вашим требованиям к производительности

Для вашего конкретного примера с массивом имен, самым простым решением будет: const uniqueNames = [...new Set(names)]; это создает массив только с уникальными значениями, сохраняя исходный порядок первого вхождения.