Какой способ поиска дубликатов быстрее?

Какой способ поиска дубликатов быстрее?

Давайте еще раз посмотрим как убрать дубликаты в массиве с помощью нашего любимого JavaScript. Да, тема изъезжена десять раз. Но… вы когда нибудь смотрели как быстро работает тот или иной способ? А на большом количестве данных? Эта статья как раз и посвящена данному вопросу.

Начнем с хорошо знакомого filter. У нас есть исходный массив:

const names = [‘like’, ‘Ivan’, ‘Leo’, ‘like’, ‘Anna’];

Способ первый, воспользуемся для начала filter, и отфильтруем наш массив:

const getUniqueByFilter = (names) => names.filter((item, i) => names.indexOf(item) === i); console.log(getUniqueByFilter(names)); // 'like', 'Ivan', 'Leo', 'Anna'

Как видим, это прекрасно работает и дает нужный результат, но есть еще несколько способов сделать подобное. Например, воспользоваться таким отличным инструментом, как reduce, он отлично работает при сложных операциях над массивом, здесь мы его применим в наших целях вот так:

const getUniqueByReduce = (names) => ( names.reduce((unique, item) => { return unique.includes(item) ? unique : [...unique, item] }, [] ) ); console.log(getUniqueByReduce(names)); // 'like', 'Ivan', 'Leo', 'Anna'

Согласитесь, выглядит не плохо.

И наконец более современный способ с привлечением нового типа в JavaScript — Set, который значительно уменьшает количество кода:

const getUniqueBySet = (name) => [...new Set(names)]; console.log(getUniqueBySet(name)); // 'like', 'Ivan', 'Leo', 'Anna'

Это наверное один из самых коротких способов по количеству кода.Больше интересного у меня на канале.

А что, если взять старый добрый цикл for и применить его:

const getUniqueByFor = (names) => { var array = []; for (var i = 0, l = array.length; i < l; i++) { if (array.indexOf(array[i]) === -1) { array.push(this[i]); } } return array; } console.log(getUniqueByFor(names)); // 'like', 'Ivan', 'Leo', 'Anna'

Согласен, количество кода не радует, но цель посмотреть, что сейчас наиболее эффективно работает, эксперимент — есть эксперимент.

Для тестирования я возьму последнюю версию Chrome, на момент написания — это 74.0.37 и будем тестировать на MacBook Pro. Код для тестирования можно посмотреть здесь.

Для эксперимента созданы несколько массивов, длинной 100, 500, 1000 и 5000 элементов. По сути — это цифры по возрастанию и каждый десятый элемент заменен на строку ‘string’, которую нам нужно и убрать.

new Array(sample) .fill('') .map((i, k) => k) .map(i => {return (i % 10 === 0) ? 'string' : i})

Затем, поочередно запускаем вышеперечисленные функции. Для чистоты эксперимента делаем это 5000 раз и с помощью window.performance.now() высчитываем время выполнения операции.

Итак, что у нас получается.

Для массива в 100 элементов, среднее(максимальное) выполнение:

filter — 0,011(0,325)ms

reduce — 0,02(0,4)ms

set — 0,005(0,275)ms

for — 0,009(1,185)ms

Теперь для массива в 500 элементов:

filter — 0.146(0.475)ms

reduce — 0,385(0.865)ms

set — 0,019(0.35)ms

for — 0.148(0.465)ms

Ну и наконец для 5000, чтобы проверить на достаточно приличном количестве данных. Это занимает какое-то время и машина немного задумывается на данном моменте, но результат такой:

filter — 13.747(18.550)ms

reduce — 32,325(86.790)ms

set — 0,253(0.915)ms

for — 18.880(12.483)ms

Что же, результаты и распределение победителей не сильно отличается при увеличении нагрузки. Как мы видим reduce в данном случае в разы проигрывает старому доброму for и filter и в сотню раз медленнее Set. Однозначно можно исключить его при фильтрации дубликатов.

Итого

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

Вторым эффективным способом остается filter, который отлично справляется задачей и делает это за вполне приемлемое время. Не сильно отстает от него и for, хотя изначально в него слабо верилось.

Если понравилась история, то подпишись на мой Телеграм канал

11
Начать дискуссию