From 7a003fd0533eca97cce4d6b9a07cfd046103f42d Mon Sep 17 00:00:00 2001 From: Nikolai Lopin Date: Mon, 7 Jun 2021 00:12:54 +0200 Subject: [PATCH 1/5] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D0=B8=D1=82=20=D1=81=D1=82=D0=B0=D1=82=D1=8C=D1=8E=20=C2=ABArr?= =?UTF-8?q?ay.reduce=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .yaspeller.json | 1 + js/doka/array-reduce/index.md | 135 ++++++++++++++++++ .../array-reduce/practice/windrushfarer.md | 67 +++++++++ 3 files changed, 203 insertions(+) create mode 100644 js/doka/array-reduce/index.md create mode 100644 js/doka/array-reduce/practice/windrushfarer.md diff --git a/.yaspeller.json b/.yaspeller.json index 0ad0c2a904..3d7e7dec03 100644 --- a/.yaspeller.json +++ b/.yaspeller.json @@ -264,6 +264,7 @@ "ребайндинг", "ревью(ер|ера|)", "регистронезависимые", + "ред(ь|)юсер", "резолвинг", "реквест(ом|ов|ы|ами|у|)", "рендер(а|е|ить|много|)", diff --git a/js/doka/array-reduce/index.md b/js/doka/array-reduce/index.md new file mode 100644 index 0000000000..ad1bfc88a1 --- /dev/null +++ b/js/doka/array-reduce/index.md @@ -0,0 +1,135 @@ +--- +title: "Array.reduce" +name: array-reduce +authors: + - windrushfarer +summary: + - редюсер редьюсер + - свёртка +--- + +## Кратко + +Метод массива `reduce` позволяет превратить массив в любое другое значение с помощью переданной функции-колбэка и начального значения. Функция-колбэк будет вызвана для каждого элемента массива и всегда должна возвращать результат. + +## Пример + +```js +const nums = [1, 2, 3, 4, 5, 6, 7, 8] + +// Находим сумму элементов +const sum = nums.reduce(function (currentSum, currentNumber) { + return currentSum + currentNumber +}, 0) // 36 + +const users = [ + { id: "1", name: "John" }, + { id: "2", name: "Anna" }, + { id: "3", name: "Kate" }, +] + +// Создаем новый объект с ключом в виде id и значением в виде имени юзера +const usernamesById = users.reduce(function (result, user) { + return { + ...result, + [user.id]: user.name, + } +}, {}) // { '1': 'John', '2': 'Anna' , '3': 'Kate' } +``` + +Интерактивный пример: + +

+ See the Pen + reduce by Egor Ogarkov (@Windrushfarer) + on CodePen. +

+ + +## Как пишется + +Метод `reduce` принимает два параметра: функцию-колбэк и начальное значение для аккумулятора. + +Сама функция-колбэк может принимать четыре параметра: + +- `acc` — текущее значение аккумулятора +- `item` — элемент массива в текущей итерации +- `index` — индекс текущего элемента +- `arr` — сам массив, который мы перебираем + +```js +const nums = [1, 2, 3, 4, 5, 6, 7, 8] + +// Не забываем, что аккумулятор идет первым! +function findAverage(acc, item, index, arr) { + const sum = acc + item + + // В конце вычисляем среднеарифметическое делением на кол-во элементов + if (index === arr.length - 1) { + return sum / arr.length + } + + return sum +} + +const average = nums.reduce(findAverage, 0) // 4.5 +``` + +Ключом к успешному использованию `reduce` является внимательно следить за порядком аргументов и не забывать возвращать значение. + +Использование `reduce` похоже на методы [forEach](/js/doka/array-foreach), [map](/js/doka/array-map) и [filter](/js/doka/array-filter), в которые так же передаётся функция-колбэк. Однако в `reduce` есть дополнительный аргумент — это текущее аккумулируемое значение. При этом можно заметить, что порядок аргументов так же немного изменён. + +Функция обязательно должна возвращать значение, т.к в каждой следующей итерации значение в `acc` будет результатом, который вернулся на предыдущем шаге. Логичный вопрос, который может здесь возникнуть, — какое значение принимает `acc` во время первой итерации? Им будет являться то самое начальное значение, которые передаётся вторым аргументом в метод `reduce` + +## Как это понять + +Метод `reduce` крайне полезен, когда мы хотим с помощью манипуляции значениями массива вычислить какое-то новое значение. Такую операцию называют **агрегацией**. Таким образом у нас появляется мощный инструмент для обработки данных, например это может быть нахождение суммы величин в массиве или группировка в другие типы данных. + +Главной особенностью `reduce`, которую важно запомнить, является наличие **аккумулятора**. Аккумулятор — это и есть то новое вычисляемое значение. Во время выполнения функции-колбэка нужно обязательно возвращать его значение, т.к оно обязательно попадает в следующую итерацию, где так же будет использоваться для дальнейших вычислений. Таким образом мы можем представить аккумулятор как переменную, значение которой можно поменять в каждой новой итерации. С помощью второго аргумента в `reduce` эта переменная получает своё начальное значение. + +```js +// Задача: вычислить сумму денег на всех счетах +const bankAccounts = [ + { id: "123", amount: 19 }, + { id: "345", amount: 33 }, + { id: "567", amount: 4 }, + { id: "789", amount: 20 }, +] + +const totalAmount = bankAccounts.reduce( + // Аргумент sum является аккумулятором, в нем храним промежуточное значение + function (sum, currentAccount) { + // Каждую итерацию берем текущее значение и складываем его с количеством + // денег на текущем счету + return sum + currentAccount.amount + }, + 0 // Начальное значение, которые инициализирует аккумулятор +) // Получим 76 +``` + +Чтобы понять этот момент можно ещё посмотреть на код, который делает то же самое, но уже без `reduce`: + +```js +// Задача: вычислить сумму денег на всех счетах +const bankAccounts = [ + { id: "123", amount: 19 }, + { id: "345", amount: 33 }, + { id: "567", amount: 4 }, + { id: "789", amount: 20 }, +] + +// Определяем где будем хранить сумму, это в нашем случае является аккумулятором, +// здесь же определяем начальное значение аккумулятора +let totalAmount = 0 + +for (let i = 0; i < bankAccounts.length; i++) { + const currentAccount = bankAccounts[i] + + // В каждой итерации прибавляем к текущей сумме количество денег на счету + totalAmount += currentAccount.amout +} + +totalAmount // Будет так же равен 76 +``` + +И в том и в том другом примере у нас аккумулятор, где хранится текущее значение и кладётся новое, есть вычисление нового значение. Только `reduce` позволяет сделать это в одном месте и более понятном декларативном стиле. diff --git a/js/doka/array-reduce/practice/windrushfarer.md b/js/doka/array-reduce/practice/windrushfarer.md new file mode 100644 index 0000000000..0fef4f2314 --- /dev/null +++ b/js/doka/array-reduce/practice/windrushfarer.md @@ -0,0 +1,67 @@ +--- +tags: + - practice +permalink: false +--- + +🛠 `reduce` действительно часто применяется для того, чтобы провести математическую операцию для всех элементов массиве и получить в итоге какой-то результат, потому стоит помнить о нем + +🛠 Если вы хотите применить подряд несколько операций `filter` и `map`, то с помощью `reduce` их можно объединить в одной функции. Иногда это может быть необходимо в целях производительности, т.к в этом случае будет всего один проход по массиву, вместо нескольких в зависимости от количества вызываемых методов. Но стоит помнить, что такой способ не всегда будет хорошо читаться + +```js +// Задача: выбрать четные, вычислить их квадраты и отобрать из них числа больше 50 +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +function filterEven(num) { + return num % 2 === 0 +} + +function square(num) { + return num * num +} + +function filterGreaterThanFifty(num) { + return num > 50 +} + +// Применяем несколько методов +const result = numbers + .filter(filterEven) + .map(square) + .filter(filterGreaterThanFifty) // [64, 100] + +// Через один reduce +const result = numbers.reduce(function (res, num) { + if (filterEvens(num)) { + const squared = square(num) + + if (filterGreaterThanFifty(squared)) { + res.push(squared) + } + } + + return res +}, []) // [64, 100] +``` + +🛠 Часто встречается использование `reduce` для нормирования значений. Например, для превращения массива с данными пользователем в объект, где ключом будет id пользователя, а значением исходный объект. Таким образом можно быстро получать значение объект-пользователя по id, обратившись по ключу к объекту, вместо поиска по массиву: + +```js +const users = [ + { id: "123", name: "Vasiliy", age: 18 }, + { id: "345", name: "Anna", age: 22 }, + { id: "567", name: "Igor", age: 20 }, + { id: "789", name: "Irina", age: 24 }, +] + +const usersById = users.reduce(function (result, user) { + result[user.id] = { + name: user.name, + age: user.age, + } + + return result +}, {}) // Получим объект с данными пользователем + +usersById["567"] // { name: 'Igor', age: 20 } +``` From f337a86daaf47bdeb73e1a2166b5a56157a354ac Mon Sep 17 00:00:00 2001 From: Vadim Makeev Date: Mon, 7 Jun 2021 01:25:11 +0300 Subject: [PATCH 2/5] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=BE=D1=81=D0=B8=D1=82?= =?UTF-8?q?=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=20=D0=B8=D0=B7=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../array-reduce/practice/windrushfarer.md | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/js/doka/array-reduce/practice/windrushfarer.md b/js/doka/array-reduce/practice/windrushfarer.md index 0fef4f2314..cfd512c0a4 100644 --- a/js/doka/array-reduce/practice/windrushfarer.md +++ b/js/doka/array-reduce/practice/windrushfarer.md @@ -4,12 +4,13 @@ tags: permalink: false --- -🛠 `reduce` действительно часто применяется для того, чтобы провести математическую операцию для всех элементов массиве и получить в итоге какой-то результат, потому стоит помнить о нем +🛠 `reduce` действительно часто применяется для того, чтобы провести математическую операцию для всех элементов массиве и получить в итоге какой-то результат, потому стоит помнить о нём. -🛠 Если вы хотите применить подряд несколько операций `filter` и `map`, то с помощью `reduce` их можно объединить в одной функции. Иногда это может быть необходимо в целях производительности, т.к в этом случае будет всего один проход по массиву, вместо нескольких в зависимости от количества вызываемых методов. Но стоит помнить, что такой способ не всегда будет хорошо читаться +🛠 Если вы хотите применить подряд несколько операций `filter` и `map`, то с помощью `reduce` их можно объединить в одной функции. Иногда это может быть необходимо в целях производительности, поскольку в этом случае будет всего один проход по массиву, вместо нескольких в зависимости от количества вызываемых методов. Но стоит помнить, что такой способ не всегда будет хорошо читаться. + +Задача: выбрать чётные, вычислить их квадраты и отобрать из них числа больше 50. ```js -// Задача: выбрать четные, вычислить их квадраты и отобрать из них числа больше 50 const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] function filterEven(num) { @@ -23,14 +24,21 @@ function square(num) { function filterGreaterThanFifty(num) { return num > 50 } +``` -// Применяем несколько методов +Применяем несколько методов: + +```js const result = numbers .filter(filterEven) .map(square) - .filter(filterGreaterThanFifty) // [64, 100] + .filter(filterGreaterThanFifty) +// [64, 100] +``` + +Через один `reduce`: -// Через один reduce +```js const result = numbers.reduce(function (res, num) { if (filterEvens(num)) { const squared = square(num) @@ -41,10 +49,11 @@ const result = numbers.reduce(function (res, num) { } return res -}, []) // [64, 100] +}, []) +// [64, 100] ``` -🛠 Часто встречается использование `reduce` для нормирования значений. Например, для превращения массива с данными пользователем в объект, где ключом будет id пользователя, а значением исходный объект. Таким образом можно быстро получать значение объект-пользователя по id, обратившись по ключу к объекту, вместо поиска по массиву: +🛠 Часто встречается использование `reduce` для нормирования значений. Например, для превращения массива с данными пользователем в объект, где ключом будет id пользователя, а значением исходный объект. Таким образом можно быстро получать значение объект-пользователя по `id`, обратившись по ключу к объекту, вместо поиска по массиву: ```js const users = [ @@ -61,7 +70,9 @@ const usersById = users.reduce(function (result, user) { } return result -}, {}) // Получим объект с данными пользователем +}, {}) +// Получим объект с данными пользователем -usersById["567"] // { name: 'Igor', age: 20 } +usersById["567"] +// { name: 'Igor', age: 20 } ``` From 8a5d8dabea8cf10b6750f00ba214c603a2146e02 Mon Sep 17 00:00:00 2001 From: Vadim Makeev Date: Mon, 7 Jun 2021 01:32:42 +0300 Subject: [PATCH 3/5] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=BE=D1=81=D0=B8=D1=82?= =?UTF-8?q?=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=20=D0=B8=D0=B7=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/doka/array-reduce/index.md | 56 ++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/js/doka/array-reduce/index.md b/js/doka/array-reduce/index.md index ad1bfc88a1..dd034711de 100644 --- a/js/doka/array-reduce/index.md +++ b/js/doka/array-reduce/index.md @@ -4,7 +4,8 @@ name: array-reduce authors: - windrushfarer summary: - - редюсер редьюсер + - редюсер + - редьюсер - свёртка --- @@ -20,8 +21,11 @@ const nums = [1, 2, 3, 4, 5, 6, 7, 8] // Находим сумму элементов const sum = nums.reduce(function (currentSum, currentNumber) { return currentSum + currentNumber -}, 0) // 36 +}, 0) +// 36 +``` +```js const users = [ { id: "1", name: "John" }, { id: "2", name: "Anna" }, @@ -34,7 +38,8 @@ const usernamesById = users.reduce(function (result, user) { ...result, [user.id]: user.name, } -}, {}) // { '1': 'John', '2': 'Anna' , '3': 'Kate' } +}, {}) +// { '1': 'John', '2': 'Anna' , '3': 'Kate' } ``` Интерактивный пример: @@ -72,23 +77,25 @@ function findAverage(acc, item, index, arr) { return sum } -const average = nums.reduce(findAverage, 0) // 4.5 +const average = nums.reduce(findAverage, 0) +// 4.5 ``` Ключом к успешному использованию `reduce` является внимательно следить за порядком аргументов и не забывать возвращать значение. -Использование `reduce` похоже на методы [forEach](/js/doka/array-foreach), [map](/js/doka/array-map) и [filter](/js/doka/array-filter), в которые так же передаётся функция-колбэк. Однако в `reduce` есть дополнительный аргумент — это текущее аккумулируемое значение. При этом можно заметить, что порядок аргументов так же немного изменён. +Использование `reduce` похоже на методы [`forEach`](/js/doka/array-foreach), [`map`](/js/doka/array-map) и [`filter`](/js/doka/array-filter), в которые так же передаётся функция-колбэк. Однако в `reduce` есть дополнительный аргумент — это текущее аккумулируемое значение. При этом можно заметить, что порядок аргументов так же немного изменён. -Функция обязательно должна возвращать значение, т.к в каждой следующей итерации значение в `acc` будет результатом, который вернулся на предыдущем шаге. Логичный вопрос, который может здесь возникнуть, — какое значение принимает `acc` во время первой итерации? Им будет являться то самое начальное значение, которые передаётся вторым аргументом в метод `reduce` +Функция обязательно должна возвращать значение, поскольку в каждой следующей итерации значение в `acc` будет результатом, который вернулся на предыдущем шаге. Логичный вопрос, который может здесь возникнуть, — какое значение принимает `acc` во время первой итерации? Им будет являться то самое начальное значение, которые передаётся вторым аргументом в метод `reduce`. ## Как это понять Метод `reduce` крайне полезен, когда мы хотим с помощью манипуляции значениями массива вычислить какое-то новое значение. Такую операцию называют **агрегацией**. Таким образом у нас появляется мощный инструмент для обработки данных, например это может быть нахождение суммы величин в массиве или группировка в другие типы данных. -Главной особенностью `reduce`, которую важно запомнить, является наличие **аккумулятора**. Аккумулятор — это и есть то новое вычисляемое значение. Во время выполнения функции-колбэка нужно обязательно возвращать его значение, т.к оно обязательно попадает в следующую итерацию, где так же будет использоваться для дальнейших вычислений. Таким образом мы можем представить аккумулятор как переменную, значение которой можно поменять в каждой новой итерации. С помощью второго аргумента в `reduce` эта переменная получает своё начальное значение. +Главной особенностью `reduce`, которую важно запомнить, является наличие **аккумулятора**. Аккумулятор — это и есть то новое вычисляемое значение. Во время выполнения функции-колбэка нужно обязательно возвращать его значение, поскольку оно обязательно попадает в следующую итерацию, где так же будет использоваться для дальнейших вычислений. Таким образом мы можем представить аккумулятор как переменную, значение которой можно поменять в каждой новой итерации. С помощью второго аргумента в `reduce` эта переменная получает своё начальное значение. + +Задача: вычислить сумму денег на всех счетах. ```js -// Задача: вычислить сумму денег на всех счетах const bankAccounts = [ { id: "123", amount: 19 }, { id: "345", amount: 33 }, @@ -97,39 +104,48 @@ const bankAccounts = [ ] const totalAmount = bankAccounts.reduce( - // Аргумент sum является аккумулятором, в нем храним промежуточное значение + // Аргумент sum является аккумулятором, + // в нём храним промежуточное значение function (sum, currentAccount) { - // Каждую итерацию берем текущее значение и складываем его с количеством - // денег на текущем счету + // Каждую итерацию берём текущее значение + // и складываем его с количеством денег + // на текущем счету return sum + currentAccount.amount }, - 0 // Начальное значение, которые инициализирует аккумулятор -) // Получим 76 + // Начальное значение, + // которые инициализирует аккумулятор + 0 +) +// Получим 76 ``` -Чтобы понять этот момент можно ещё посмотреть на код, который делает то же самое, но уже без `reduce`: +Чтобы понять этот момент можно ещё посмотреть на код, который делает то же самое, но уже без `reduce`. + +Задача: вычислить сумму денег на всех счетах. ```js -// Задача: вычислить сумму денег на всех счетах const bankAccounts = [ { id: "123", amount: 19 }, { id: "345", amount: 33 }, { id: "567", amount: 4 }, { id: "789", amount: 20 }, ] +``` -// Определяем где будем хранить сумму, это в нашем случае является аккумулятором, -// здесь же определяем начальное значение аккумулятора +Определяем где будем хранить сумму, это в нашем случае является аккумулятором, здесь же определяем начальное значение аккумулятора. + +```js let totalAmount = 0 for (let i = 0; i < bankAccounts.length; i++) { const currentAccount = bankAccounts[i] - // В каждой итерации прибавляем к текущей сумме количество денег на счету + // В каждой итерации прибавляем + // к текущей сумме количество денег на счету totalAmount += currentAccount.amout } - -totalAmount // Будет так же равен 76 ``` +`totalAmount` здесь будет так же равен 76. + И в том и в том другом примере у нас аккумулятор, где хранится текущее значение и кладётся новое, есть вычисление нового значение. Только `reduce` позволяет сделать это в одном месте и более понятном декларативном стиле. From 3fbcb9451fab26abb66b6c77ad9e2d5ddaf0e3c9 Mon Sep 17 00:00:00 2001 From: Vadim Makeev Date: Tue, 8 Jun 2021 19:35:19 +0300 Subject: [PATCH 4/5] =?UTF-8?q?=D0=A3=D0=B1=D0=B8=D1=80=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=20=D0=BD=D0=B5=D0=BD=D1=83=D0=B6=D0=BD=D1=8B=D0=B9=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/doka/array-reduce/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/js/doka/array-reduce/index.md b/js/doka/array-reduce/index.md index dd034711de..59d92eef39 100644 --- a/js/doka/array-reduce/index.md +++ b/js/doka/array-reduce/index.md @@ -1,6 +1,5 @@ --- title: "Array.reduce" -name: array-reduce authors: - windrushfarer summary: From 798d560dfd6055328a541ae6ab0f821c1e968ce4 Mon Sep 17 00:00:00 2001 From: Nikolai Lopin Date: Fri, 11 Jun 2021 07:32:55 +0200 Subject: [PATCH 5/5] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D1=8F=D0=B5=D1=82?= =?UTF-8?q?=20=D1=81=D0=BB=D0=BE=D0=B2=D0=BE=20=D1=80=D0=B5=D0=B4=D1=8E?= =?UTF-8?q?=D1=81=D0=B5=D1=80=20=D0=B8=D0=B7=20=D1=81=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=80=D1=8F=20=D0=B8=20=D1=81=D1=82=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .yaspeller.json | 2 +- js/doka/array-reduce/index.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.yaspeller.json b/.yaspeller.json index 3d7e7dec03..5d5649fd3b 100644 --- a/.yaspeller.json +++ b/.yaspeller.json @@ -264,7 +264,7 @@ "ребайндинг", "ревью(ер|ера|)", "регистронезависимые", - "ред(ь|)юсер", + "редьюсер", "резолвинг", "реквест(ом|ов|ы|ами|у|)", "рендер(а|е|ить|много|)", diff --git a/js/doka/array-reduce/index.md b/js/doka/array-reduce/index.md index 59d92eef39..d12ffc8a63 100644 --- a/js/doka/array-reduce/index.md +++ b/js/doka/array-reduce/index.md @@ -3,7 +3,6 @@ title: "Array.reduce" authors: - windrushfarer summary: - - редюсер - редьюсер - свёртка ---