Потому что этот молоток на самом деле является швейцарским армейским ножом!
Когда я впервые столкнулся с функцией reduce
в Javascript, мне показалось странным ее использование. У меня был код с различными массивами повсюду, но прямо здесь мне нужно было создать объект, а не массив. Это означало, что я не мог использовать методы forEach
и map
, к которым я привык - вместо этого мне пришлось использовать reduce
.
Итак, во-первых, это было странно, потому что он принимал массив и выдавал объект. Мне казалось, что я перемещаюсь из одного мира в другой. И в дополнение к этому вы добавляете для передачи эту странную «аккумуляторную» переменную.
Вряд ли я знал, что на самом деле я только что наткнулся на удивительный инструмент ... :)
Использование reduce
: основы
Основная идея функции reduce
состоит в том, чтобы перебирать список значений и использовать их для постепенного «заполнения аккумулятора». Этот аккумулятор представляет собой переменную любого типа, которую вы хотите, которую вы просто инициализируете в самом начале сокращения, а затем изменяете (или просто пропускаете) на каждой итерации. Обычное использование reduce
- это вариант использования, о котором я говорил во вступлении: создание объекта из массива вещей.
Например, предположим, что у вас есть список сообщений в блоге. Эти сообщения в блоге хранятся в виде массива объектов, каждый из которых выглядит следующим образом:
{ id: '20210512-my-first-article', title: 'My first article', pubdate: '2021-05-12T14:20:19.528622', excerpt: 'This is my first article - yay!', // ... additional info }
Многие блоги работают аналогичным образом: вы сначала попадаете на домашнюю страницу со списком доступных статей, а когда вы щелкаете по большой миниатюре или заголовку одной статьи, вы затем перенаправляетесь на специальную страницу. Если вы посмотрите URL-адрес этой новой страницы в своем браузере, он, скорее всего, будет содержать уникальный идентификатор, соответствующий выбранному вами сообщению.
Таким образом, более чем вероятно, что на веб-сайте есть какая-то логика, в которой он считывает идентификатор в URL-адресе и использует его для получения информации и содержания сообщения в блоге. Это означает, что вам нужно найти нужный объект в массиве сообщений вашего блога.
Теперь вы можете использовать find
метод Javascript, чтобы просмотреть свой массив и найти первый элемент с правильным идентификатором. Он очень прост в использовании, хорошо читается в вашем коде и, прежде всего, позволяет вам использовать ваши данные как есть. Однако это не самый эффективный способ поиска публикации.
Если у вас всего дюжина сообщений, у вас не будет никаких проблем. Но если вы собираете архивы за десятилетия и у вас есть тысячи и тысячи статей, вы можете столкнуться с некоторыми проблемами времени загрузки. Это потому, что метод find
аккуратный, но он должен пройти по всем элементам в списке один за другим и проверять ваше условие, пока не найдется один из них. Так что, если ваша статья окажется последней, вы буквально пройдете тысячи проверок, прежде чем наконец найдете нужный.
Вот небольшой пример JS-скрипта, который создает массивы сообщений в блогах различного размера, а затем использует метод find
для получения сообщения с наибольшим идентификатором (то есть последнего элемента в списке):
Если мы запустим его, мы получим те результаты, которые ясно показывают, как размер коллекции влияет на время вычислений:
n = 10 Compute time: 0.078ms n = 100 Compute time: 0.008ms n = 1000000 Compute time: 14.524ms n = 10000000 Compute time: 140.235ms
Общее практическое правило для времени отклика API - держать его меньше 1 секунды, иначе пользователь заметит задержку (и помните, что здесь мы уже ждали, чтобы дождаться страницы статьи - теперь мы на странице мы ожидаем, что он будет загружен мгновенно). Итак, очевидно, что наша программа не прошла бы мимо, если бы у нас были десятки миллионов статей… (конечно, это большое количество статей, но, эй, вы должны быть готовы ко всему!).
Как я уже отмечал в недавней статье об итерациях Python, независимо от того, какой язык программирования вы используете, выбор правильной структуры данных может быть ключом к решению проблемы с производительностью.
В сценарии пост-итерации нашего блога было бы гораздо интереснее иметь пары ключ-значение, которые сопоставляют идентификатор с соответствующей информацией о посте. А в Javascript это делается с помощью объекта.
И все это для того, чтобы, наконец, представить наш reduce
пример! Очень распространенный метод решения этой проблемы «перестройки структуры данных» - сохранить «объектную версию» сообщений вашего блога в коде веб-сайта, чтобы всякий раз, когда вам нужно получить доступ к статье по ее идентификатору, вы могли очень быстро получить ее в этом оптимизированное отображение.
Функция reduce
требует, чтобы вы определили 3 вещи:
- массив, который вы повторяете
- функция, применяемая на каждой итерации: она принимает как ваш аккумулятор, так и текущий элемент в вашей повторяющейся коллекции
- начальное значение для аккумулятора
Вот пример того, как преобразовать массив сообщений нашего блога в объект, используя идентификатор статьи в качестве ключа:
Первоначальное преобразование может быть долгим, но как только оно будет выполнено, вы получите доступ ко всему в мгновение ока!
Примечание: область алгоритмики изучает, помимо прочего, структуры данных и связанную с ними сложность для различных операций. Здесь мы говорим о доступе к определенному элементу в структуре. С массивом и его методом find
эта операция считается (наихудший сценарий) за O (n), что означает, что она растет линейно с количеством элементов в списке - если у вас вдвое больше элементов и ваш статья является последней, вам придется пройти в два раза больше проверок, прежде чем вы ее доберетесь. С другой стороны, прямое отображение объекта находится в O (1), то есть доступ занимает постоянное время и не зависит от количества элементов.
Использование reduce
для вычисления суммы, среднего ...
В reduce
замечательно то, что он не только способен создавать объекты, но и действительно может «накапливать» все, что вы хотите. Это особенно полезно, когда вы хотите вычислить сумму массива значений, или среднее значение, или даже некоторую забавную конкатенацию этих значений:
Примечание: я кратко упомянул задачи CodinGame в предыдущем Quickie Dev, но вы, вероятно, увидите, насколько это может быть полезно для решения некоторых из их головоломок при работе с Javascript! ;)
Однако я должен отметить, что reduce
, хотя и очень легко читается, не обязательно является наиболее эффективным вариантом для вычисления сумм, средних и т. Д. Простой цикл for может быть намного лучше.
Использование reduce
для фильтрации
Еще одно применение reduce
может заключаться в фильтрации. По моему опыту, это часто сочетается с другими преобразованиями (в противном случае вы можете просто использовать filter
функцию). Поскольку вы накапливаете вещи, вы также можете пропустить итерацию и напрямую вернуть свой аккумулятор, не изменяя его для этого конкретного элемента.
Например, предположим, что у вас есть список чисел, и вас интересует только квадрат четных. Вы можете сначала применить filter
, а затем map
, но для этого потребуется n + t операций (где n - начальное количество элементов, а t - количество отфильтрованных элементов).
С помощью reduce вы уменьшаете количество операций до n, поскольку выполняете все сразу:
Заключение
В этой статье была представлена действительно приятная функция reduce
JS, которую можно применять во многих случаях использования, и она в большей степени меняет форму, чем я думал изначально. Вы должны знать, что если вы ищете истинную производительность, собственные циклы обычно являются более быстрым методом, чем новая функция JS, такая как map
, filter
или reduce
, как вспоминает в этой статье Ю. Кадишай. Но эти конструкции очень удобочитаемы, и иногда это дороже, чем простое старое вычислительное время ...
А вы? У вас есть советы и рекомендации по использованию reduce
Javascript? Не стесняйтесь реагировать в комментариях! :)
Если вам понравилась эта статья, вы можете найти больше сообщений в блоге о технологиях, искусственном интеллекте и программировании на моем сайте :)