Метод reduce в TypeScript применение функции сразу к двум значениям массива и уменьшение их до

Содержание материала:

Метод reduce()

Array.reduce()

Применяет функцию к аккумулятору и каждому значению массива (слева-направо), сводя его к одному значению.

Спецификация: ECMAScript 5.1 (ECMA-262)

Синтаксис

Параметры

Возвращаемое значение

Возвращает накопленный результат с последнего вызова функции обратного вызова.

Описание

Метод reduce() используется для вычисления на основе массива какого-либо единого значения, иначе говорят «для свёртки массива».

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

При первом вызове функции, параметры total и currentValue могут принимать одно из двух значений:

  • Если при вызове reduce( ) передан аргумент initialValue , то значение total будет равным значению initialValue , а значение currentValue будет равным первому значению в массиве.
  • Если аргумент initialValue не задан, то значение total будет равным первому значению в массиве, а значение currentValue будет равным второму значению в массиве.

Если массив пустой, а аргумент initialValue не указан, то будет сгенерировано исключение TypeError .
В случае, если массив состоит только из одного элемента и аргумент initialValue не указан, или если аргумент initialValue указан, но массив пустой, то будет возвращено одно это значение. При этом функции callback вызываться не будет.

Разберём работу метода reduce() на примере.

Например, в качестве «свёртки» мы хотим получить сумму всех элементов массива numbers:

Как это работает? Методы forEach, map, reduce для работы с массивами в Javascript

Доброго времени суток, дорогие читатели! Рада вас приветствовать на страницах своего блога.

Сегодня речь снова пойдет о вопросах и задачках, которые задают на собеседованиях frontend-разработчику на знание Javascript, в частности стандарта ES6. Да-да, такое часто бывает спрашивают.

Напишите аналог встроенного метода forEach для работы с массивами

Для решения этой задачи необходимо вспомнить, что делает встроенный метод forEach :

  1. Обходит массив по всей длине до последнего элемента.
  2. Вызывает функцию для каждого элемента.
  3. Функция, которая вызывается на каждой итерации, принимает в себя 3 параметра:
    • item – очередной элемент массива.
    • i – его порядковый номер (в массивах порядковый номер элемента начинается с 0).
    • arr – сам массив, который перебирается.

Соответственно, функция-аналог будет принимать в себя 2 аргумента: массив, который необходимо перебрать, и функцию, которая вызывается внутри цикла для каждого элемента.

Практическое использование forEach

На практике метод forEach ничего не возвращает, его используют только для перебора, как более «элегантный» вариант, чем обычный цикл for . Поэтому если руки так и чешутся сократить количество строчек кода, то можно использовать такую запись:

Или еще короче. И так тоже будет работать:

Так как мы используем только одну запись, а стрелочные функции нам это позволяют.

Напишите аналог встроенного метода map для работы с массивами

Метод map применяется, когда необходимо создать новый массив,

который будет состоять из результатов вызова callback(item, i, arr) для каждого элемента arr .

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

  1. Мы создаем новый массив.
  2. Циклом for перебираем массив, который пришел в функцию в качестве аргумента.
  3. На следующем этапе мы выполняем fn для каждого элемента массива array и складываем его в переменную item.
  4. Добавляем переменную в конец массива results .
  5. Возвращаем results .

Напишите аналог встроенного метода reduce для работы с массивами

И на закуску работа со встроенным методом reduce. Он используется для последовательной обработки каждого элемента массива с сохранением промежуточного результата. Метод применяет функцию callback по очереди к каждому элементу массива слева направо, сохраняя при этом промежуточный результат.
Задача метода:

  1. Для каждого элемента массива запустить функцию.
  2. Промежуточный результат передавать первым аргументом далее.

Аналог функции reduce:

Ну как-то так. Кому не лень, может сам поиграть с методами массивов. До встречи в новых статьях!

1 комментарий для “ Как это работает? Методы forEach, map, reduce для работы с массивами в Javascript ”

У оригинального reduce еще есть проверка на случай, если массив пустой и initialValue не был указан. В этом случае выбрасывается TypeError.

Добавить комментарий Отменить ответ

Я рада, что Вы входите в число продвинутых пользователей, использующих AdBlock.

Однако мой скромный сайт существует за счет показа и кликов по рекламе.

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

JavaScript метод reduce()

Определение и применение

JavaScript метод reduce() позволяет вызвать функцию обратного вызова один раз для каждого элемента массива в порядке возрастания индекса (слева-направо). Возвращаемое значение функции предоставляется как аргумент при следующем вызове функции, в результате чего возвращаемое значение последнего вызова становится возвращаемым значением метода (значение вычисляется из элементов массива).

Для вызова функции обратного вызова в порядке убывания индекса (справа-налево) вы можете воспользоваться методом reduceRight().

Обращаю Ваше внимание, что функция обратного вызова, переданная в качестве параметра метода reduce() не будет вызвана для удалённых, или пропущенных элементов массива. Метод reduce() не изменяет массив для которого он был вызван (хотя функция обратного вызова может это делать).

Диапазон элементов, обрабатываемых с помощью метода reduce() устанавливается перед первым вызовом функции обратного вызова. Если элементы были добавлены к массиву после её вызова, то на таких элементах функция вызвана не будет.

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

Поддержка браузерами

Метод Chrome

Firefox Opera Safari IExplorer Edge
reduce() Да Да Да Да 9.0 Да

JavaScript синтаксис:

Версия JavaScript

Значения параметров

Параметр Описание
callback Функция обратного вызова, которая будет выполнена один раз для каждого элемента массива в порядке возрастания индекса.

Функция принимает следующие параметры:

  • total — значение, используемое в качестве первого аргумента первого вызова функции обратного вызова (если указан параметр метода initialValue), или будет равно первому значению в массиве, а параметр currentValue будет равен второму. Параметр выступает в роли накопителя возвращаемых значений и соответствует ранее возвращенному значению функции обратного вызова. Обязательный параметр.
  • currentValue — значение текущего элемента. Обязательный параметр.
  • currentIndex — индекс массива текущего элемента. Начинается с индекса 0, если указан параметр initialValue, в противном случае с индекса 1. Необязательный параметр.
  • arr — массив, к которому принадлежит текущий элемент (по которому происходит проход). Необязательный параметр.

Возвращаемое значение функции предоставляется как аргумент при следующем вызове функции, в результате чего возвращаемое значение последнего вызова становится возвращаемым значением метода (значение вычисляется из элементов массива).

initialValue Значение, используемое в качестве первого аргумента первого вызова функции обратного вызова. Если начальное значение не указано, то будет использован первый элемент массива. Необязательный параметр.

Исключения

Тип исключения Описание
TypeError Возникает в том случае, если метод вызывается на пустом массиве и при этом не указано значение параметра initialValue.

Пример использования

В следующем примере мы с Вами детально рассмотрим принцип работы JavaScript метода reduce() и получим сумму всех элементов массива:

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

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

Давайте рассмотрим следующий пример:

В этом примере с помощью метода reduce() мы получили сумму всех элементов массива в порядке возрастания индекса. Обратите внимание, что при первом вызове функции обратного вызова параметр total будет равен первому значению в массиве, а параметр currentValue будет равен второму.

Давайте рассмотрим следующий пример, в котором задействуем второй параметр метода reduce():

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

Методы массивов

Массивы предоставляют множество методов. Чтобы было проще, в этой главе они разбиты на группы.

Добавление/удаление элементов

Мы уже знаем методы, которые добавляют и удаляют элементы из начала или конца:

  • arr.push(. items) – добавляет элементы в конец,
  • arr.pop() – извлекает элемент из конца,
  • arr.shift() – извлекает элемент из начала,
  • arr.unshift(. items) – добавляет элементы в начало.

splice

Как удалить элемент из массива?

Так как массивы – это объекты, то можно попробовать delete :

Вроде бы, элемент и был удалён, но при проверке оказывается, что массив всё ещё имеет 3 элемента arr.length == 3 .

Это нормально, потому что всё, что делает delete obj.key – это удаляет значение с данным ключом key . Это нормально для объектов, но для массивов мы обычно хотим, чтобы оставшиеся элементы сдвинулись и заняли освободившееся место. Мы ждём, что массив станет короче.

Поэтому для этого нужно использовать специальные методы.

Метод arr.splice(str) – это универсальный «швейцарский нож» для работы с массивами. Умеет всё: добавлять, удалять и заменять элементы.

Он начинает с позиции index , удаляет deleteCount элементов и вставляет elem1, . elemN на их место. Возвращает массив из удалённых элементов.

Этот метод проще всего понять, рассмотрев примеры.

Начнём с удаления:

Легко, правда? Начиная с позиции 1 , он убрал 1 элемент.

В следующем примере мы удалим 3 элемента и заменим их двумя другими.

Здесь видно, что splice возвращает массив из удалённых элементов:

Метод splice также может вставлять элементы без удаления, для этого достаточно установить deleteCount в 0 :

В этом и в других методах массива допускается использование отрицательного индекса. Он позволяет начать отсчёт элементов с конца, как тут:

slice

Метод arr.slice намного проще, чем похожий на него arr.splice .

Он возвращает новый массив, в который копирует элементы, начиная с индекса start и до end (не включая end ). Оба индекса start и end могут быть отрицательными. В таком случае отсчёт будет осуществляться с конца массива.

Это похоже на строковый метод str.slice , но вместо подстрок возвращает подмассивы.

Можно вызвать slice и вообще без аргументов: arr.slice() создаёт копию массива arr . Это часто используют, чтобы создать копию массива для дальнейших преобразований, которые не должны менять исходный массив.

concat

Метод arr.concat создаёт новый массив, в который копирует данные из других массивов и дополнительные значения.

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

В результате мы получаем новый массив, включающий в себя элементы из arr , а также arg1 , arg2 и так далее…

Если аргумент argN – массив, то все его элементы копируются. Иначе скопируется сам аргумент.

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

…Но если объект имеет специальное свойство Symbol.isConcatSpreadable , то он обрабатывается concat как массив: вместо него добавляются его числовые свойства.

Для корректной обработки в объекте должны быть числовые свойства и length :

Перебор: forEach

Метод arr.forEach позволяет запускать функцию для каждого элемента массива.

Например, этот код выведет на экран каждый элемент массива:

А этот вдобавок расскажет и о своей позиции в массиве:

Результат функции (если она вообще что-то возвращает) отбрасывается и игнорируется.

Поиск в массиве

Далее рассмотрим методы, которые помогут найти что-нибудь в массиве.

indexOf/lastIndexOf и includes

Методы arr.indexOf, arr.lastIndexOf и arr.includes имеют одинаковый синтаксис и делают по сути то же самое, что и их строковые аналоги, но работают с элементами вместо символов:

  • arr.indexOf(item, from) ищет item , начиная с индекса from , и возвращает индекс, на котором был найден искомый элемент, в противном случае -1 .
  • arr.lastIndexOf(item, from) – то же самое, но ищет справа налево.
  • arr.includes(item, from) – ищет item , начиная с индекса from , и возвращает true , если поиск успешен.

Обратите внимание, что методы используют строгое сравнение === . Таким образом, если мы ищем false , он находит именно false , а не ноль.

Если мы хотим проверить наличие элемента, и нет необходимости знать его точный индекс, тогда предпочтительным является arr.includes .

Кроме того, очень незначительным отличием includes является то, что он правильно обрабатывает NaN в отличие от indexOf/lastIndexOf :

find и findIndex

Представьте, что у нас есть массив объектов. Как нам найти объект с определённым условием?

Здесь пригодится метод arr.find.

Его синтаксис таков:

Функция вызывается по очереди для каждого элемента массива:

  • item – очередной элемент.
  • index – его индекс.
  • array – сам массив.

Если функция возвращает true , поиск прерывается и возвращается item . Если ничего не найдено, возвращается undefined .

Например, у нас есть массив пользователей, каждый из которых имеет поля id и name . Попробуем найти того, кто с >:

В реальной жизни массивы объектов – обычное дело, поэтому метод find крайне полезен.

Обратите внимание, что в данном примере мы передаём find функцию item => item. >, с одним аргументом. Это типично, дополнительные аргументы этой функции используются редко.

Метод arr.findIndex – по сути, то же самое, но возвращает индекс, на котором был найден элемент, а не сам элемент, и -1 , если ничего не найдено.

filter

Метод find ищет один (первый попавшийся) элемент, на котором функция-колбэк вернёт true .

На тот случай, если найденных элементов может быть много, предусмотрен метод arr.filter(fn).

Синтаксис этого метода схож с find , но filter возвращает массив из всех подходящих элементов:

Преобразование массива

Перейдём к методам преобразования и упорядочения массива.

Метод arr.map является одним из наиболее полезных и часто используемых.

Он вызывает функцию для каждого элемента массива и возвращает массив результатов выполнения этой функции.

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

sort(fn)

Вызов arr.sort() сортирует массив на месте, меняя в нём порядок элементов.

Он возвращает отсортированный массив, но обычно возвращаемое значение игнорируется, так как изменяется сам arr .

Не заметили ничего странного в этом примере?

Порядок стал 1, 15, 2 . Это неправильно! Но почему?

По умолчанию элементы сортируются как строки.

Буквально, элементы преобразуются в строки при сравнении. Для строк применяется лексикографический порядок, и действительно выходит, что «2» > «15» .

Чтобы использовать наш собственный порядок сортировки, нам нужно предоставить функцию в качестве аргумента arr.sort() .

Функция должна для пары значений возвращать:

Например, для сортировки чисел:

Теперь всё работает как надо.

Давайте возьмём паузу и подумаем, что же происходит. Упомянутый ранее массив arr может быть массивом чего угодно, верно? Он может содержать числа, строки, объекты или что-то ещё. У нас есть набор каких-то элементов. Чтобы отсортировать его, нам нужна функция, определяющая порядок, которая знает, как сравнивать его элементы. По умолчанию элементы сортируются как строки.

Метод arr.sort(fn) реализует общий алгоритм сортировки. Нам не нужно заботиться о том, как он работает внутри (в большинстве случаев это оптимизированная быстрая сортировка). Она проходится по массиву, сравнивает его элементы с помощью предоставленной функции и переупорядочивает их. Всё, что остаётся нам, это предоставить fn , которая делает это сравнение.

Кстати, если мы когда-нибудь захотим узнать, какие элементы сравниваются – ничто не мешает нам вывести их на экран:

В процессе работы алгоритм может сравнивать элемент с другими по нескольку раз, но он старается сделать как можно меньше сравнений.

На самом деле от функции сравнения требуется любое положительное число, чтобы сказать «больше», и отрицательное число, чтобы сказать «меньше».

Это позволяет писать более короткие функции:

Помните стрелочные функции? Можно использовать их здесь для того, чтобы сортировка выглядела более аккуратной:

Будет работать точно так же, как и более длинная версия выше.

reverse

Метод arr.reverse меняет порядок элементов в arr на обратный.

Он также возвращает массив arr с изменённым порядком элементов.

split и join

Ситуация из реальной жизни. Мы пишем приложение для обмена сообщениями, и посетитель вводит имена тех, кому его отправить, через запятую: Вася, Петя, Маша . Но нам-то гораздо удобнее работать с массивом имён, чем с одной строкой. Как его получить?

Метод str.split(delim) именно это и делает. Он разбивает строку на массив по заданному разделителю delim .

В примере ниже таким разделителем является строка из запятой и пробела.

У метода split есть необязательный второй числовой аргумент – ограничение на количество элементов в массиве. Если их больше, чем указано, то остаток массива будет отброшен. На практике это редко используется:

Вызов split(s) с пустым аргументом s разбил бы строку на массив букв:

Вызов arr.join(glue) делает в точности противоположное split . Он создаёт строку из элементов arr , вставляя glue между ними.

reduce/reduceRight

Если нам нужно перебрать массив – мы можем использовать forEach , for или for..of .

Если нам нужно перебрать массив и вернуть данные для каждого элемента – мы используем map .

Методы arr.reduce и arr.reduceRight похожи на методы выше, но они немного сложнее. Они используются для вычисления какого-нибудь единого значения на основе всего массива.

Функция применяется по очереди ко всем элементам массива и «переносит» свой результат на следующий вызов.

  • previousValue – результат предыдущего вызова этой функции, равен initial при первом вызове (если передан initial ),
  • item – очередной элемент массива,
  • index – его индекс,
  • array – сам массив.

При вызове функции результат её вызова на предыдущем элементе массива передаётся как первый аргумент.

Звучит сложновато, но всё становится проще, если думать о первом аргументе как «аккумулирующем» результат предыдущих вызовов функции. По окончании он становится результатом reduce .

Этот метод проще всего понять на примере.

Тут мы получим сумму всех элементов массива всего одной строкой:

Здесь мы использовали наиболее распространённый вариант reduce , который использует только 2 аргумента.

Давайте детальнее разберём, как он работает.

  1. При первом запуске sum равен initial (последний аргумент reduce ), то есть 0 , а current – первый элемент массива, равный 1 . Таким образом, результат функции равен 1 .
  2. При втором запуске sum = 1 , и к нему мы добавляем второй элемент массива ( 2 ).
  3. При третьем запуске sum = 3 , к которому мы добавляем следующий элемент, и так далее…

Поток вычислений получается такой:

В виде таблицы, где каждая строка –- вызов функции на очередном элементе массива:

sum current result
первый вызов 1 1
второй вызов 1 2 3
третий вызов 3 3 6
четвёртый вызов 6 4 10
пятый вызов 10 5 15

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

Мы также можем опустить начальное значение:

Результат – точно такой же! Это потому, что при отсутствии initial в качестве первого значения берётся первый элемент массива, а перебор стартует со второго.

Таблица вычислений будет такая же за вычетом первой строки.

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

Поэтому рекомендуется всегда указывать начальное значение.

Метод arr.reduceRight работает аналогично, но проходит по массиву справа налево.

Array.isArray

Массивы не образуют отдельный тип языка. Они основаны на объектах.

Поэтому typeof не может отличить простой объект от массива:

…Но массивы используются настолько часто, что для этого придумали специальный метод: Array.isArray(value). Он возвращает true , если value массив, и false , если нет.

Большинство методов поддерживают «thisArg»

Почти все методы массива, которые вызывают функции – такие как find , filter , map , за исключением метода sort , принимают необязательный параметр thisArg .

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

Вот полный синтаксис этих методов:

Значение параметра thisArg становится this для func .

Например, вот тут мы используем метод объекта army как фильтр, и thisArg передаёт ему контекст:

Если бы мы в примере выше использовали просто users.filter(army.canJoin) , то вызов army.canJoin был бы в режиме отдельной функции, с this=undefined . Это тут же привело бы к ошибке.

Вызов users.filter(army.canJoin, army) можно заменить на users.filter(user => army.canJoin(user)) , который делает то же самое. Последняя запись используется даже чаще, так как функция-стрелка более наглядна.

Итого

Шпаргалка по методам массива:

Для добавления/удаления элементов:

  • push (. items) – добавляет элементы в конец,
  • pop() – извлекает элемент с конца,
  • shift() – извлекает элемент с начала,
  • unshift(. items) – добавляет элементы в начало.
  • splice(pos, deleteCount, . items) – начиная с индекса pos , удаляет deleteCount элементов и вставляет items .
  • slice(start, end) – создаёт новый массив, копируя в него элементы с позиции start до end (не включая end ).
  • concat(. items) – возвращает новый массив: копирует все члены текущего массива и добавляет к нему items . Если какой-то из items является массивом, тогда берутся его элементы.

Для поиска среди элементов:

  • indexOf/lastIndexOf(item, pos) – ищет item , начиная с позиции pos , и возвращает его индекс или -1 , если ничего не найдено.
  • includes(value) – возвращает true , если в массиве имеется элемент value , в противном случае false .
  • find/filter(func) – фильтрует элементы через функцию и отдаёт первое/все значения, при прохождении которых через функцию возвращается true .
  • findIndex похож на find , но возвращает индекс вместо значения.

Для перебора элементов:

  • forEach(func) – вызывает func для каждого элемента. Ничего не возвращает.

Для преобразования массива:

  • map(func) – создаёт новый массив из результатов вызова func для каждого элемента.
  • sort(func) – сортирует массив «на месте», а потом возвращает его.
  • reverse() – «на месте» меняет порядок следования элементов на противоположный и возвращает изменённый массив.
  • split/join – преобразует строку в массив и обратно.
  • reduce(func, initial) – вычисляет одно значение на основе всего массива, вызывая func для каждого элемента и передавая промежуточный результат между вызовами.
  • Array.isArray(arr) проверяет, является ли arr массивом.

Обратите внимание, что методы sort , reverse и splice изменяют исходный массив.

Изученных нами методов достаточно в 99% случаев, но существуют и другие.

Функция fn вызывается для каждого элемента массива аналогично map . Если какие-либо/все результаты вызовов являются true , то метод возвращает true , иначе false .

arr.fill(value, start, end) – заполняет массив повторяющимися value , начиная с индекса start до end .

arr.copyWithin(target, start, end) – копирует свои элементы, начиная со start и заканчивая end , в собственную позицию target (перезаписывает существующие).

Полный список есть в справочнике MDN.

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

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

Всякий раз, когда вам будет необходимо что-то сделать с массивом, а вы не знаете, как это сделать – приходите сюда, смотрите на таблицу и ищите правильный метод. Примеры помогут вам всё сделать правильно, и вскоре вы быстро запомните методы без особых усилий.

Задачи

Переведите текст вида border-left-width в borderLeftWidth

Напишите функцию camelize(str) , которая преобразует строки вида «my-short-string» в «myShortString».

То есть дефисы удаляются, а все слова после них получают заглавную букву.

Array.prototype.reduce()

На этой странице

Метод reduce() применяет функцию reducer к каждому элементу массива (слева-направо), возвращая одно результирующее значение.

Источник этого интерактивного примера хранится в репозитории GitHub. Если вы хотите внести свой вклад в проект интерактивных примеров, скопируйте https://github.com/mdn/interactive-examples и отправьте нам запрос на перенос.

Синтаксис

Параметры

Описание

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

При первом вызове функции, параметры accumulator и currentValue могут принимать одно из двух значений. Если при вызове reduce() передан аргумент initialValue , то значение accumulator будет равным значению initialValue , а значение currentValue будет равным первому значению в массиве. Если аргумент initialValue не задан, то значение accumulator будет равным первому значению в массиве, а значение currentValue будет равным второму значению в массиве.

Если массив пустой и аргумент initialValue не указан, будет брошено исключение TypeError . Если массив состоит только из одного элемента (независимо от его положения в массиве) и аргумент initialValue не указан, или если аргумент initialValue указан, но массив пустой, то будет возвращено одно это значение, без вызова функции callback .

Предположим, что reduce() используется следующим образом:

Функция обратного вызова будет вызвана четыре раза, аргументы и возвращаемое значение при каждом вызове будут следующими:

previousValue currentValue index array возвращаемое значение
первый вызов 1 1 [0, 1, 2, 3, 4] 1
второй вызов 1 2 2 [0, 1, 2, 3, 4] 3
третий вызов 3 3 3 [0, 1, 2, 3, 4] 6
четвёртый вызов 6 4 4 [0, 1, 2, 3, 4] 10

Значение, возвращённое методом reduce() будет равным последнему результату выполнения функции обратного вызова ( 10 ).

Если же вы зададите начальное значение initialValue , результат будет выглядеть так:

accumulator currentValue index array возвращаемое значение
первый вызов 10 [0, 1, 2, 3, 4] 10
второй вызов 10 1 1 [0, 1, 2, 3, 4] 11
третий вызов 11 2 2 [0, 1, 2, 3, 4] 13
четвёртый вызов 13 3 3 [0, 1, 2, 3, 4] 16
пятый вызов 16 4 4 [0, 1, 2, 3, 4] 20

Значение, возвращённое методом reduce() на этот раз, конечно же, будет равным 20 .

Примеры

Суммирование всех значений в массиве

Суммирование значений в массиве объектов

Чтобы суммировать значения, содержащиеся в массиве объектов, вы должны указать initialValue , чтобы каждый элемент смог пройти через callback .

TypeScript and array reduce function

Do you know what does array reduce function do in TypeScript? Can you provide a simple example of usage?

I search on Google and TypeScript language specification but could not find any decent explanation and examples.

4 Answers 4

It’s actually the JavaScript array reduce function rather than being something specific to TypeScript.

As described in the docs: Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value.

Here’s an example which sums up the values of an array:

The alert box will show 6 .

Just a note in addition to the other answers.

If an initial value is supplied to reduce then sometimes its type must be specified, viz:-

Метод массива reduce

Чтобы просмотреть это видео, включите JavaScript и используйте веб-браузер, который поддерживает видео в формате HTML5

JavaScript, часть 1: основы и функции

Half Faded Star

JavaScript — единственный язык программирования, который поддерживается всеми браузерами. Его знает любой веб-разработчик. Если вы собираетесь изучать JavaScript, этот курс — отличное начало. Вы освоите основы и научитесь писать простые программы. Авторы курса — разработчики из Яндекса. У курса есть продолжение.

Рецензии

Half Faded Star

Быстрый старт в тему, короткие, но информативные видео, очень интересный курс для новичков.

Отличный курс. Прекрасное сочетание теории и практики. Теория, тест, практика.

Преподаватели

Чистяков Денис

Сергей Жигалов

Текст видео

Следующей функцией для работы с массивами, которую мы рассмотрим, будет функция reduce. Это, пожалуй, одна из самых гибких и одна из самых сложных для понимания функций для работы с массивами. Но будьте внимательны, и она вам во многом пригодится. Итак, эта функция, так же как и другие функции для работы с массивами, принимает на вход в качестве первого параметра функцию обратного вызова, а в качестве второго параметра — так называемый аккумулятор и его начальное значение для вашей обработки данных. Что происходит далее? Функция обратного вызова вызывается, в качестве первого параметра мы каждый раз получаем значение аккумулятора. А в качестве второго параметра мы получаем элемент массива, на котором мы сейчас находимся. После этого на каждом новом шаге мы должны обработать значение из аккумулятора, то есть предыдущее значение, которое было ранее, что-то с ним сделать. В данном случае мы хотим посчитать сумму лайков, которые есть в нашем массиве из объектов твитов, и просуммировать их, чтобы посчитать суммарное количество лайков в нашей ленте. Мы берем целое число из нашего объекта, на котором мы сейчас находимся, и добавляем его к нашему аккумулятору, возвращая это значение из нашей функции обратного вызова. Таким образом, на каждом шаге цикла, начиная с 0, мы прибавляем значение количества лайков внутри нашего текущего элемента к нашему аккумулятору и возвращаем новое значение из аккумулятора. И пройдя по всему массиву, мы посчитаем суммарное количество лайков в виде целого числа и вернем его из функции reduce. Но в качестве начального значения может быть не только целое число, может быть произвольный тип данных. Это может быть, например, массив. Для чего это может быть полезно? Например, мы хотим взять наш массив с твитами и выбрать из него все хештеги, которые есть в нашей ленте. Для этого мы будем на каждой итерации нашей операции reduce добавлять к этому массиву, изначально пустому, хештеги, которые находятся в текущем элементе на текущей итерации. А если у текущего элемента нет хештегов, то это будет пустой массив. После этого мы конкатенируем наш текущий аккумулятор с этим массивом и возвращаем его. Таким образом, на каждом шаге мы добавляем новые хештеги из текущего элемента на текущем шаге итерации в наш аккумулятор и возвращаем обновленный аккумулятор. После этого мы получим один большой массив, который будет сочетать все хештеги из нашего изначального массива с объектами. Далее, мы можем использовать объект в качестве начального значения для аккумулятора. Для чего это может быть полезно? Например, мы хотим посчитать статистику использования тех или иных хештегов в нашей ленте. И в качестве начального объекта, от которого мы будем плясать, мы возьмем уже схлопнутый, соединенный воедино массив со всеми хештегами. И в качестве элемента в данном случае будет выступать текущий хештег в массиве, который будет являться строчкой. Что мы делаем дальше? На вход, как мы помним, в аккумулятор мы передаем в качестве начального значения пустой объект. Далее, у этого объекта мы проверяем наличие свойства, которое является нашим текущим хештегом. И если такового свойства нет, то мы инициируем это свойство у нашего объекта с нулевым значением. После этого мы суммируем для каждого элемента объекта по имени хештега наш счетчик. Таким образом, в конце, пройдя все шаги итерирования, мы посчитаем количество использований каждого хештега внутри нашей ленты и вернем его каждый раз из нашей функции обратного вызова в качестве аккумулятора. И так же, как с map, filter или forEach можно использовать reduce в качестве цепочек. И мы можем взять изначальное значение, в нашем случае твиты, сначала схлопнуть их в один большой массив, состоящий только из хештегов, а потом посчитать по нему статистику. И в качестве результата мы получим нашу статистику в виде объекта со счетчиком по каждому хештегу. Опять-таки код получается разделенный, аккуратный, каждая функция выполняет свое независимое действие. А в следующей лекции мы рассмотрим дополнительные методы для работы со строчками и с массивами, для которых у нас не хватило времени в основной части.

Метод reduce в TypeScript: применение функции сразу к двум значениям массива и уменьшение их до одного

1660 просмотра

2 ответа

7160 Репутация автора

Скажем, у меня есть следующий складной интерфейс:

И тогда я хочу реализовать это для массива:

Но компилятор жалуется на:

Аргумент типа ‘(b: A, a: A) => A’ не может быть назначен параметру типа ‘(previousValue: A, currentValue: A, currentIndex: number, array: A []) => A’. Типы параметров ‘a’ и ‘currentValue’ несовместимы. Тип «А» нельзя назначить типу «А». Существуют два разных типа с этим именем, но они не связаны.

Я не понимаю, как здесь есть два разных типа A .

Ответы (2)

1 плюс

154398 Репутация автора

Проще увидеть, что происходит, если вы измените имена общих типов:

Теперь вы получите Type ‘R’ is not assignable to type ‘A’ . array.reduce использует разные типы для текущего значения и предыдущего значения, поэтому у вас есть тип A (универсальный тип из вашего интерфейса) и тип R из вашей getArrayFold функции.

Вы на самом деле не передали универсальный тип A в reduce , поэтому он считает, что он A из интерфейса, что, по сути, означает, что он не может определить, каким должен быть тип.

Я нашел способ сделать это, чтобы ваш интерфейс мог указывать тип A и F :

Теперь вы можете написать свою функцию массива как

Когда вы звоните, вы можете сделать

Это даст вам безопасность типов , так что вы не можете использовать в 0 качестве значения или .toFixed на a / b свойства или тому подобное.

1 плюс

3447 Репутация автора

Есть две ошибки:

  • Вам необходимо указать тип массива. Вы не можете получить его от одного дженерика Array , вам нужно представить и то, T и другое Array .
  • Ваш тип для функции, которая потребляется, reduce не подходит. Правильный (previousValue: A, currentValue: F) => A

Если вы предоставляете начальное значение с типом (например string ) для уменьшения функции, параметр previousValue всегда совпадает с начальным .

Table of Contents #

Функции — фундаментальные строительные блоки каждого приложения на JavaScript. С их помощью строятся слои абстракции, реализуются классы, сокрытие данных и модули. Хотя в TypeScript есть и классы, и пространства имен, и модули, функции по-прежнему играют ключевую роль в описании того, как все работает. Кроме того, TypeScript добавляет несколько новых возможностей к стандартным JavaScript-функциям, и делает работу с ними проще.

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

Напомним, как эти два варианта выглядят в JavaScript:

Как и в JavaScript, функции могут обращаться к переменным вне своего тела. Когда такое происходит, говорят, что функция «захватывает» переменные. Хотя объяснять то, как это работает и каковы подводные камни данной техники — не задача данной статьи, важно иметь четкое понимание этого механизма, чтобы работать с JavaScript и TypeScript.

Типы функций #

Добавление типов к функции

Добавим к функции из предыдущих простых примеров типы:

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

Пишем тип функции

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

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

Если типы параметров совпадают, то тип считается подходящим для функции, и не важно, какие имена были даны параметрам в описании типа функции.

Вторая часть типа функции — тип возвращаемого значения. На него указывает толстая стрелка ( => ) между параметрами и типом возвращаемого значения. Как писалось выше, эта часть необходима, и поэтому, если функция не возвращает ничего, в качестве типа возвращаемого значения нужно указать void .

Отметим, что параметров и типа возвращаемого значения вполне достаточно, чтобы описать тип функции. Переменные, которые функция захватывает, в ее типе не отражаются. По сути, захваченные переменные — часть так называемого «скрытого состояния» функции, и они не являются частью её API.

Выведение типов

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

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

Опциональные параметры и параметры по умолчанию #

В TypeScript считается, что каждый параметр функции обязателен. Это не значит, что ей нельзя передать null или undefined : это означает, что при вызове функции компилятор проверит, задал ли пользователь значение для каждого ее параметра. Кроме того, компилятор считает, что никакие параметры, кроме указанных, не будут передаваться. Проще говоря, число передаваемых параметров должно совпадать с числом параметров, которые ожидает функция.

В JavaScript все параметры необязательны, и пользователи могут пропускать их, если нужно. В таких случаях значение пропущенных параметров принимается за undefined . В TypeScript тоже можно добиться этого: для этого в конце параметра, который нужно сделать необязательным, добавляется ? . К примеру, мы хотим сделать необязательным lastName из предыдущего примера:

Все необязательные параметры должны идти после обязательных. Если бы первый параметр ( firstName ) нужно было сделать опциональным вместо lastName , то порядок параметров в функции пришлось бы изменить, чтобы firstName оказался последним.

Также TypeScript позволяет указать для параметра значение, которое он будет принимать, если пользователь пропустит его или передаст undefined . Такие параметры называются параметрами со значением по умолчанию или просто параметрами по умолчанию. Возьмем предыдущий пример и зададим для lastName значение по умолчанию, равное «Smith» .

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

будут иметь одинаковый тип (firstName: string, lastName?: string) => string . Значение по умолчанию для параметра lastName в описании типа функции исчезает, и остается лишь тот факт, что последний параметр необязателен.

В отличие от простых опциональных параметров, параметры по умолчанию не обязательно должны находиться после обязательных параметров. Если после параметра по умолчанию будет идти обязательный, то придется явно передать undefined , чтобы задать значение по умолчанию. К примеру, последний пример можно переписать, используя для firstName только параметр по умолчанию:

Остаточные параметры (rest parameters) #

Обязательные, опциональные и параметры по умолчанию имеют одну общую для всех черту — они описывают по одному параметру за раз. В некоторых случаях нужно работать с несколькими параметрами, рассматривая их как группу; а иногда заранее неизвестно, сколько параметров функция будет принимать. В JavaScript с аргументами можно работать напрямую, используя переменную arguments , которая доступна внутри любой функции.

В TypeScript можно собрать аргументы в одну переменную:

Остаточные параметры (rest parameters) можно понимать как неограниченное число необязательных параметров. При передаче аргументов для остаточных параметров их можно передать столько, сколько угодно; а можно и вообще ничего не передавать. Компилятор построит массив из переданных аргументов, присвоит ему имя, которое указано после многоточия ( . ), и сделает его доступным внутри функции.

Многоточие используется и при описании типа функции с остаточными параметрами:

Научиться правильно использовать this в JavaScript — нечто вроде обряда посвящения в разработчики. Поскольку TypeScript — это надмножество JavaScript, программистам на TypeScript также нужно понимать, как использовать this и как замечать, когда this используется неправильно. К счастью, TypeScript позволяет обнаруживать неправильное использование this с помощью нескольких приемов. Если вам только предстоит разобраться с тем, как работает this , то для начала прочтите статью Yehuda Katz Понятие о вызове функций в JavaScript и «this». Эта статья очень хорошо объясняет, как работает this «под капотом», поэтому здесь мы рассмотрим только основы.

Прим. переводчика — на русском языке по данной теме можно посоветовать прочесть соответствующую статью из учебника javascript.ru, а также Ключевое слово this в JavaScript.

this и стрелочные функции

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

Давайте посмотрим на пример:

Обратите внимание, что createCardPicker — функция, которая возвращает функцию. Если попытаться запустить этот пример, то мы получим ошибку вместо ожидаемого сообщения. Так происходит по той причине, что this , которая используется в функции, созданной createCardPicker , указывает на window , а не на объект deck . Все это из-за того, что cardPicker() вызывается сама по себе. При использовании подобного синтаксиса, когда функция вызывается не как метод, и при том на самом верхнем уровне программы, this будет указывать на window . (Замечание: в режиме соответствия стандартам ( strict mode ) в таких случаях this будет иметь значение undefined , а не window ).

Можно исправить это, удостоверившись в том, что функция привязана к правильному значению this , прежде чем возвращать ее. В таком случае, независимо от того, как она будет использоваться в дальнейшем, ей все равно будет доступен оригинальный объект deck . Чтобы сделать это, нужно изменить функцию, и использовать синтаксис стрелочной функции из стандарта ECMAScript 6. Стрелочные функции захватывают значение this таким, каким оно было на момент ее создания (а не во время вызова):

Что еще лучше, если передать компилятору флаг —noImplicitThis , то TypeScript будет выдавать предупреждение, если вы сделаете подобную ошибку. Он укажет на то, что this в выражении this.suits[pickedSuit] имеет тип any .

Параметры this

К сожалению, тип выражения this.suits[pickedSuit] по прежнему any , поскольку this берется из функционального выражения внутри объектного литерала. Чтобы исправить это, можно явно указать this в качестве параметра. Параметр this — это «фальшивый» параметр, который идет первым в списке параметров функции:

Добавим к предыдущему примеру несколько интерфейсов: Card и Deck , чтобы сделать типы более понятными и простыми для повторного использования:

Теперь компилятор знает, что функция createCardPicker ожидает, что будет вызвана на объекте с типом Deck . Это значит, что тип значения this теперь — Deck , а не any , и флаг —noImplicitThis не будет выдавать ошибок.

Параметры this для функций обратного вызова

Также можно столкнуться с ошибками, связанными с this в функциях обратного вызова, когда функции передаются в библиотеку, которая позже будет их вызывать. Поскольку переданная функция будет вызвана библиотекой как обычная функция, у this будет значение undefined . Приложив некоторые усилия, можно использовать параметр this , чтобы предотвратить подобные ошибки. Во-первых, разработчик библиотеки должен сопроводить тип функции обратного вызова параметром this :

this: void означает, что addClickListener предполагает, что функция onclick не требует this . Во-вторых, код, который вызывается, нужно также сопроводить параметром this :

Когда this указан, это явно отражает тот факт, что onClickBad должна вызываться на экземпляре класса Handler . Теперь TypeScript обнаружит, что addClickListener требует функцию с this: void . Чтобы исправить эту ошибку, изменим тип this :

Так как в функции onClickGood указано, что тип this — void , ее можно передать в addClickListener . Конечно, это означает и то, что теперь в ней нельзя использовать this.info . Но если нужно и то, и другое, то придется использовать стрелочную функцию:

Это будет работать, поскольку стрелочные функции не захватывают this из контекста, в котором выполняются, и их можно свободно передавать там, где ожидается функция с this: void . Недостаток такого решения в том, что для каждого объекта Handler будет создаваться своя стрелочная функция. Методы же, напротив, создаются только однажды, ассоциируются с прототипом Handler , и являются общими для всех объектов этого класса.

Перегрузки #

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

В этом примере функция pickCard возвращает две разные вещи в зависимости от того, что было ей передано. Если пользователь передал объект, который представляет колоду карт, функция выберет одну из карт. Если же пользователь передает карту, функция определит, какую карту он выбрал. Но как описать такое поведение с помощью системы типов?

Нужно указать для одной функции несколько типов, создав список перегрузок. Этот список компилятор будет использовать для проверок при вызове функции. Создадим список перегрузок, который описывает, что принимает функция pickCard и что она возвращает.

Изменив код таким образом, мы получаем возможность вызывать функцию pickCard , совершая проверку типов.

Для того, чтоб выбрать правильную проверку типов, компилятор производит действия, схожие с аналогичными действиями в JavaScript. Он просматривает список перегрузок, начиная с первого элемента, и сопоставляет параметры функций. Если параметры подходят, то компилятор выбирает эту перегрузку как верную. Поэтому, как правило, перегрузки функций упорядочивают от наиболее специфичных к наименее специфичным.

Обратите внимание, что участок кода function pickCard(x): any не входит в список перегрузок; в этом списке всего два элемента, один из которых принимает object , а другой — число. Вызов pickCard с параметрами любых других типов приведет к ошибке.

Метод reduce в TypeScript: применение функции сразу к двум значениям массива и уменьшение их до одного

С приходом в процесс разработки новой версии ECMAScript 6 со всеми её восхитительными возможностями можно легко забыть, что и в старом добром ECMAScript 5 есть на что обратить внимание. А именно на встроенные методы map() и reduce(), которые содержит JavaScript Array объект.

Если вы всё ещё не используете методы map() и reduce() , то самое время начать это делать. Все современные платформы поддерживают ECMAScript 5 по умолчанию. Использование данных методов может сделать ваш код более чистым, читаемым и легко поддерживаемым, что в конечном итоге приведет к более изящной разработке.

Производительность

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

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

Также нужно отметить, что использование таких методов, как map() и reduce() сейчас, может иметь огромное преимущество в будущем, в том случае, если браузерные движки будут оптимизированы именно под эти методы.

Использование метода map()

Mapping – это фундаментальная техника функционального программирования для выполнения каких-либо операций над всеми элементами массива, в результате которой формируется новый массив той же размерности, но с изменённым контентом.

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

Вероятно, вы уже знаете, как сделать описанное выше используя, например, цикл for для массива. Один из вариантов цикла может выглядеть так:

Мы определили несколько вспомогательных переменных: массив с именем animals , который содержит все наши слова; пустой массив lengths для конечного результата работы цикла; переменная item , в которой временно будет содержаться каждый элемент цикла, над которым нужно произвести определённую манипуляцию. Для самого цикла мы установили переменные count – номер итерации цикла и loops – размер массива слов. Затем мы производим итерацию цикла по всем элементам массива animals . Для каждого элемента массива производится вычисление количества символов слова, и вставка полученной длины методом push() в результативный массив lengths .

Возможно, мы могли бы сделать это более кратко без использования переменной item , просто вставив длину элемента массива animals[ count ] напрямую в массив lengths без промежуточного назначения. Это помогло бы нам уменьшить количество кода, но также сделало бы весь код менее читаемым даже для такого простого примера. Аналогично, чтобы сделать этот пример более производительным, но менее очевидным, можно было использовать длину массива animals для создания массива lengths с помощью new Array( animals.length ) , а затем вставить элементы по индексу, вместо использования метода push() . Выбор способа реализации задачи должен определяться тем, как вы собираетесь использовать написанный код приложения в реальном мире.

Описанный выше способ реализации является полностью работоспособным и должен работать под любым движком JavaScript, выполняя свою работу. Но узнав, как можно сделать тоже самое с помощью метода map() , использование старого способа вам покажется громоздким и неуклюжим.

Позвольте показать вам, как можно решить нашу задачу с помощью метода map() :

В этом случае, мы опять начинаем с объявления массива animals , содержащего список слов. Однако, единственная дополнительная переменная, которую мы используем – это lengths , в которую присваивается результат работы метода map() , который, в свою очередь, возвращает результат работы анонимной функции, принимающей отдельный элемент animal массива animals и возвращающей изменённые данные. Эта анонимная функция производит необходимые для нас операции над каждым элементом массива, который содержится в переменной animal и возвращает результат вычисления длины слова. В итоге, переменная lengths становится массивом той же длины, что и оригинальный массив animals , но содержит количество символов каждого слова.

Для этого способа можно привести несколько замечаний. Во-первых, это намного короче, чем при использовании циклов. Во-вторых, нам нужно объявить значительно меньше переменных. Меньше переменных – значит уменьшение обращений к глобальной области имен и как следствие, меньше вероятность возникновения багов при взаимодействии разных частей приложения. Дополнительно, ни одна из наших переменных не изменяет свое состояние после присвоения данных. Использование неизменяемых переменных и констант – это изящный подход написания кода, применяемый в функциональном программировании.

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

Чтобы решить эти затруднения, мы можем объявить именованную функцию getLength() и использовать её таким образом:

Наш код стал заметно чище, не так ли? Простое использование mapping-техники может вывести ваш код на совершенно другой уровень.

Что такое Функтор (Functor)?

В качестве небольшого отступления, можно также упомянуть тот факт, что добавление возможности использования mapping-техники при работе с объектом array привело к тому, что в ECMAScript 5 обычный тип данных – массив, стал полноценным функтором, что делает функциональное программирование ещё более удобным.

Согласно классическому определению в функциональном программировании, функтор отвечает трём критериям:

  1. Должен содержать набор значений
  2. Должен реализовывать map-функцию для обработки каждого элемента из набора значений
  3. Map-функция должна возвращать функтор такого же размера

Для более подробного ознакомления с данной конструкцией, можно перейти к статье в Википедии: Функциональный объект

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

Метод reduce() также является новичком в ECMAScript 5 и в чём-то схож с map() , за исключением того, что производит не новый функтор, а простой результат, который может быть любого типа. Например, представим, что мы бы хотели получить сумму количества символов во всех словах, которые содержатся в массиве animals . По старинке, мы могли бы сделать это так:

После того, как мы определили начальный массив animals , мы создаём переменную total для конечной суммы и назначаем ей значение, равное нулю. Также, мы создаем переменную item , в которой будет храниться каждая итерация цикла for по массиву animals . И дополнительные переменные count – для счётчика цикла; loops – количество итераций. Затем мы запускаем цикл for , который проходит по каждому элементу массива animals , присваивая переменной item текущий элемент. И под конец, мы прибавляем длину слова к значению, хранящемуся в переменной total .

И опять же, в таком подходе, с технической точки зрения – всё в порядке. Мы начали с создания массива и закончили, получив результат. Но использования метода reduce() позволит сделать наш код более очевидным:

В этом случае, мы объявили новую переменную total и присвоили ей результат работы метода reduce() , который применили к массиву animal . Метод reduce() принимает два параметра: анонимную функцию и начальное значение, равное нулю. Затем, он проходит по каждому элементу массива, выполняя функцию для текущего элемента, в которой происходит обработка данных. И эти данные доступны на каждой итерации цикла. В нашем случае, анонимная функция принимает два параметра: сумму и то слово из массива, которое будет обработано. Сама же функция просто складывает сумму и длину обрабатываемого слова.

Важным моментом в данном случае является то, что мы установили второй параметр для метода reduce() , равным нулю. Данный параметр будет начальным значением для первого аргумента при первом запуске анонимной функции. Попробуйте самостоятельно изменить это значение, также прописав console.log( sum ); внутри метода reduce() , чтобы на практике понять его работу.

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

Получилось немного длинней, но это как раз тот случай, кода больше кода не значит – хуже. В данном случае должно быть более понятным то, что происходит при использовании метода reduce() .

В этом случае, метод reduce() принимает два параметра: функцию, которая применяется к каждому элементу массива и начальное значение для возвращаемого результата работы функции. Созданная нами именованная функция addLength() также принимает два параметра: вычисляемую сумму и обрабатываемую строку.

В заключение

Использование методов map() и reduce() на регулярной основе, даст вам возможность сделать код более чистым и универсальным, а также улучшить его поддерживаемость, что является профессиональным подходом к программированию.

Данные методы одни из нескольких, которые были добавлены в ECMAScript 5. По всей вероятности, удовлетворение разработчиков от использования подобных методов превысит тот профит, который может быть получен от обычных способов, таких, как циклы.

При создании статьи были использованы следующие источники:

Топ-пост этого месяца:  Реклама в Facebook Marketplace новый вид плейсмента
Добавить комментарий