Сегодня мы будем освоить карта , фильтр и уменьшить деконструировав и восстанавливая их с нуля.
Когда я был маленьким, я получил часы в подарок. К большому ужасу моей матери, первое, что я сделал, – это захватить самую маленькую отвертку, которую я мог найти, и разобрать ее по кусочкам. Я хотел увидеть внутреннюю часть и проверить каждую часть.
К облегчению моей матери, я смог вернуть часы обратно в его оригинальное рабочее состояние. Тщательно осмотрев внутренние, я ушел с лучшим пониманием того, что делает часы.
Сегодня я все еще люблю разбирать вещи, чтобы лучше понять их. Это практика, которую я бы также поощрял.
Начнем с рассмотрения уменьшить внешне. Сразу же я могу идентифицировать 4 части. массив , Метод , Reducer и ИСТИНАЛЬНАЯ ВАЛА Анкет
const items = [ 1, 2, 3, 4 ]
const initialValue = 0
const reducer = (accumulator, currentValue) => accumulator + currentValue
items.reduce(reducer, initialValue) //=> 10
/* \ \ \ \
array \ \ - initial value
method \
reducer
*/
Все довольно самостоятельно. Все, кроме Reducer Анкет Это требует дальнейшего срыва.
Примечание: у редукторов есть 4 параметра, пока мы проигнорируем последние 2 и сосредоточимся на аккумулятор и текущая стоимость .
Эти параметры обычно сокращены как acc и cur Анкет
const reducer = (acc, cur) => acc + cur
Поскольку вы уже знакомы для петли, я могу использовать цикл для FOR ниже, чтобы помочь продемонстрировать, что аккумулятор и CurrentValue есть и как они используются.
const items = [ 1, 2, 3, 4 ]
let acc = 0
// \
// initial value
for (let i = 0; i < items.length; i++) {
const cur = items[i]
// \
// current value
acc = acc + cur
// \
// update the accumulator
}
И вставить Reducer …
for (let i = 0; i < items.length; i++) {
const cur = items[i]
acc = reducer(acc, cur)
}
Если вы хотите увидеть больше подобных сбоев, ознакомьтесь с картой, фильтрованием, уменьшите VS для петли (синтаксис).
В примере выше, Аккумулятор это Номер , но это не должно быть Номер , это может быть любой тип.
В этом примере acc это Массив и Reducer выдвигает удвоенное значение в аккумулятор .
const items = [ 1, 2, 3, 4 ]
const reducer = (acc, cur) => {
acc.push(cur * 2)
return acc
/* \
The reducer must always return the accumulator
*/
}
let acc = []
for (let i = 0; i < items.length; i++) {
const cur = items[i]
acc = reducer(acc, cur)
}
acc //=> [ 2, 4, 6, 8 ]
В этом примере Аккумулятор является объектом, а новые значения добавляются в объект.
const items = [ 1, 2, 3, 4 ]
const reducer = (acc, cur) => {
acc[cur] = cur * 2
return acc
}
let acc = {}
for (let i = 0; i < items.length; i++) {
const cur = items[i]
acc = reducer(acc, cur)
}
acc //=> { 1:2, 2:4, 3:6, 4:8 }
Вы должны заметить между этими примерами, код цикла был идентичен. Не верите мне? Давай прокрутите обратно и проверьте! Только ИСТИНАЛЬНАЯ ВАЛА и Reducer измененный. Итак, будь то Аккумулятор это Номер , а Массив , а Объект , или какой -то другой тип … Вам нужно только изменить Начальное значение и Reducer , не петля!
Поскольку мы знаем, что для цикла никогда не меняется, это легко извлечь его в собственную функцию, уменьшить Анкет
const reduce = () => {
for (let i = 0; i < items.length; i++) {
const cur = items[i]
acc = reducer(acc, cur)
}
}
Ваш Линтер должен жаловаться на отсутствие Reducer и Предметы Итак, давайте добавим их. Мы также добавим ИСТИНАЛЬНАЯ ВАЛА Пока мы в этом.
const reduce = (items, reducer, initialValue) => {
let acc = initialValue
for (let i = 0; i < items.length; i++) {
const cur = items[i]
acc = reducer(acc, cur)
}
return acc
}
Это оно? Мы только что создали уменьшить ? Кажется слишком просто!
Ну, мы проигнорировали эти 2 дополнительных параметра в Reducer Анкет Также ИСТИНАЛЬНАЯ ВАЛА в уменьшить Должно быть необязательным, но это требуется в нашей версии. Мы доберемся до этого позже.
Можно сказать, что карта является производным уменьшить Анкет В этом случае мы можем использовать наш Reducer Сверху передайте это уменьшить и предоставить начальное значение [] . Начальное значение – [] Потому что наш результат будет Массив Анкет
const map = (items, func) => {
// |
// function to modify value
const initialValue = []
const reducer = (acc, cur) => {
acc.push(func(cur))
// |
// execute func on the currentValue
return acc
}
return reduce(items, reducer, initialValue)
}
const double = x => x * 2
map(items, double) //=> [ 2, 4, 6, 8 ]
фильтр почти точно так же, как карта . Мы просто должны изменить Reducer Чтобы фильтровать значения на основе результатов из предикат Анкет
const filter = (items, predicate) => {
// |
// if truthy, append to accumulator
const initialValue = []
const reducer = (acc, cur) => {
if (predicate(cur)) {
// |
// run predicate on currentValue
acc.push(cur)
}
return acc
}
return reduce(items, reducer, initialValue)
}
const isEven = x => x % 2 === 0
filter(items, isEven) //=> [ 2, 4 ]
ИСТИНАЛЬНАЯ ВАЛА в уменьшить должен быть необязательным. Мы должны быть в состоянии сделать это и получить результат 10 , вместо этого мы получаем НАН Анкет
const add = (acc, cur) => acc + cur const items = [ 1, 2, 3, 4 ] reduce(items, add) //=> NaN
Как бы вы сделали ИСТИНАЛЬНАЯ ВАЛА необязательный? Покажите свой код в комментариях.
Я упоминал выше, что редуктор берет 4 аргумента. Все 4 аргумента:
- Аккумулятор (аккумулятор)
- Текущее значение (CurrrentValue)
- Текущий индекс (CurrentIndex)
- Массив источника (источник)
Мы уже реализовали аккумулятор и текущая стоимость . Как бы вы реализовали CurrentIndex и Источник ? Покажите мне свой код в комментариях.
Изменить уменьшить работать с обоими Массив и Итератор Анкет Это что -то Массив Снижение не может сделать.
// range is an Iterator.
const range = require('mojiscript/list/range')
const reduce = (items, reducer, initialValue) => {
let acc = initialValue
for (let i = 0; i < items.length; i++) {
const cur = items[i]
acc = reducer(acc, cur)
}
return acc
}
const add = (acc, cur) => acc + cur
// Make this return 10
reduce(range(0)(5), add, 0)
Создать Уменьшите, что функция Это как уменьшить , но принимает дополнительную функцию, которая сломает итерацию, когда заданное условие будет выполнено. Думайте об этом как о перерыв в петле.
const predicate = (acc, cur) => acc + cur < 7
const reduce = (items, predicate, reducer, initialValue) => {
/* solution goes here */
}
Эта статья упорядочила аргументы определенным образом, чтобы быть легче читать для начинающих. Но если бы я спроектировал эти функции, чтобы быть дружественными к FP, я бы заказал аргументы как таковые:
- предикат
- редуктор
- Начальное значение
- список
После деконструкции карта , фильтр и уменьшить Чтобы узнать свои внутренние секреты, они становятся намного более доступными для нас.
Это легко увидеть, создав свой собственный уменьшить , вы можете расширить такие функции, как возможность поддержать Итератор или разорвать рано. Я пошел еще дальше с Mojiscript’s уменьшить Поддерживая асинхронный итератор а также Асинхровый редуктор Анкет
Было ли что -то, что ты хотел бы, чтобы я занялся более подробно? Вы что -то узнали, прочитав эту статью? Дай мне знать в комментариях!
Если вы любите функциональный JavaScript, следуйте за мной здесь или в Twitter @joelnet !
Оригинал: “https://dev.to/joelnet/deconstructing-map-filter-and-reduce-1d1a”