Удаление дублей из массива в JavaScript

Дубли в массиве появляются часто: из формы, ответа сервера, объединения нескольких списков или после обработки данных. Если повторяющиеся значения не нужны, их можно удалить и оставить только уникальные элементы.

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

Удаление дублей через Set

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

let numbers = [1, 2, 2, 3, 4, 4, 5]

let uniqueNumbers = new Set(numbers)

console.log(uniqueNumbers) // Set(5) {1, 2, 3, 4, 5}

В результате получается не массив, а коллекция Set. Если нужен обычный массив, можно использовать Array.from().

let numbers = [1, 2, 2, 3, 4, 4, 5]

let uniqueNumbers = Array.from(new Set(numbers))

console.log(uniqueNumbers) // [1, 2, 3, 4, 5]

То же самое часто записывают через spread-оператор.

let numbers = [1, 2, 2, 3, 4, 4, 5]

let uniqueNumbers = [...new Set(numbers)]

console.log(uniqueNumbers) // [1, 2, 3, 4, 5]

Оба варианта делают одно и то же. Можно выбрать тот, который лучше читается в вашем коде.

Порядок элементов

Set сохраняет порядок первого появления элемента в массиве. Поэтому после удаления дублей значения останутся в привычном порядке.

let tags = ['js', 'html', 'css', 'js', 'html']

let uniqueTags = [...new Set(tags)]

console.log(uniqueTags) // ['js', 'html', 'css']

Повторные ‘js’ и ‘html’ удалились, но первый порядок элементов сохранился.

Удаление дублей через filter()

Удалить дубли можно и без Set, используя метод filter(). Для этого нужно оставить только первое вхождение каждого элемента.

let numbers = [1, 2, 2, 3, 4, 4, 5]

let uniqueNumbers = numbers.filter((number, index, array) => {
  return array.indexOf(number) === index
})

console.log(uniqueNumbers) // [1, 2, 3, 4, 5]

Метод indexOf() возвращает индекс первого найденного элемента. Если текущий индекс совпадает с первым найденным, значит элемент встречается впервые.

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

Массив объектов

С объектами Set работает иначе, чем со строками и числами. Два объекта с одинаковыми свойствами все равно считаются разными, если это разные объекты в памяти.

let users = [
  { id: 1, name: 'Alex' },
  { id: 1, name: 'Alex' },
]

let uniqueUsers = [...new Set(users)]

console.log(uniqueUsers.length) // 2

Если нужно удалить дубли из массива объектов, обычно выбирают поле, по которому определяется уникальность. Например, id.

let users = [
  { id: 1, name: 'Alex' },
  { id: 2, name: 'Maria' },
  { id: 1, name: 'Alex' },
]

let uniqueUsers = users.filter((user, index, array) => {
  return array.findIndex(item => item.id === user.id) === index
})

console.log(uniqueUsers)
// [{ id: 1, name: 'Alex' }, { id: 2, name: 'Maria' }]

Здесь findIndex() ищет первое появление пользователя с таким же id. Если текущий элемент и есть первое появление, он остается в массиве.

Когда какой способ использовать

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

let roles = ['admin', 'user', 'admin']

let uniqueRoles = [...new Set(roles)]

Если массив состоит из объектов, нужно сначала решить, по какому полю объекты считаются одинаковыми: id, email, slug или другому значению.

Итого

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