JavaScript - как удалить повторяющиеся значения из массивов и получить уникальные значения

Использование методов Array.prototype.filter() и Array.prototype.indexOf()

let originalArray = [1, 2, 3, 4, 1, 2, 3, 4]

let uniqueArray = originalArray.filter((item, index, array) => {
  return array.indexOf(item) === index
})

// uniqueArray === [1, 2, 3, 4]

Основная стратегия здесь состоит в том, чтобы выполнить итерацию массива originalArray и проверить, совпадает ли индекс элемента, который мы проверяем, с индексом элемента в originalArray.

Так-как indexOf возвращает первый индекс, который он находит для данного значения, если это не дублирующее значение, тогда индекс для этого элемента должен быть таким же!

Обратите внимание, что этот метод не самый эффективный: он выполняется за квадратичное время. Поэтому, если проверяемые массивы очень большие, вы можете использовать другой метод.

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

let duplicateArray = originalArray.filter((item, index, array) => {
  return array.indexOf(item) !== index
})

Использование методов Array.prototype.reduce() и Array.prototype.includes()

let originalArray = [1, 2, 3, 4, 1, 2, 3, 4]

let uniqueArray = originalArray.reduce((unique, item) => {
  unique.includes(item) ? unique : [...unique, item]
}, [])

// uniqueArray === [1, 2, 3, 4]

Здесь стратегия состоит в том, чтобы сохранить список уникальных элементов в нашей функции «аккумулятора» (unique). Для каждого элемента originalArray мы проверяем, включает ли аккумулятор рассматриваемый элемент.

  • Если он содержит элемент, возвращает аккумулятор без внесения каких-либо изменений, фактически пропустив этот элемент.
  • Если он не содержит элемент, распределяет значения в аккумуляторе в новый массив и добавляет рассматриваемый элемент.

Array.prototype.includes возвращает логическое значение - true если значение найдено в массиве, false если нет. Данное логическое значение управляет нашим условием, определяя, что делать с каждым значением.

Я считаю этот подход менее интуитивным и трудным для чтения, но он работает работает хорошо.

Также обратите внимание, что пустой массив, который передается после функцииreduce, является начальным значением для аккумулятора, поэтому первый проход через reduce, unique является пустым массивом.

⚡ Использование ES6 объекта Set ⚡

let originalArray = [1, 2, 3, 4, 1, 2, 3, 4]

let uniqueArray = array => [...new Set(array)]

// или

let uniqueArray = Array.from(new Set(originalArray))

// uniqueArray = [1, 2, 3, 4]

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

Наборы гарантированно сохраняют порядок вставленных элементов и содержат только уникальные значения. Поэтому по определению невозможно, чтобы набор (set) содержал дубликаты!

Здесь мы вызываем конструктор объекта Set, передавая ему массив, из которого мы хотим построить набор уникальных значений. Затем, как только мы удалили все дубликаты и сохранили оставшиеся значения в нашем наборе Set, мы конвертируем обратно в массив и возвращаем результат.

Я видел некоторые обсуждения данного подхода, которые были бы немного менее производительными, если рассматриваемый массив очень большой и содержит много повторяющихся значений. Тем не менее, то же обсуждение показало, что этот подход очень эффективен в сценарии, где данные имеют очень мало дубликатов.

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