解构 Map、Filter 和 Reduce 累加器 Reduce Map Filter 其他特性 额外加分 PS 总结

2025-06-09

解构 Map、Filter 和 Reduce

累加器

减少

地图

筛选

其他功能

额外积分

聚苯乙烯

概括

拆卸相机

今天,我们将通过从头开始解构和重建来掌握mapfilter和。reduce

我小时候收到一块手表作为礼物。让我妈妈大吃一惊的是,我做的第一件事就是抓起一把我能找到的最小的螺丝刀,把它一块一块地拆开。我想看看它的内部构造,检查每一个部件。

让我妈妈松了一口气的是,我终于把手表修好了,恢复了原来的状态。彻底检查了手表的内部结构后,我对手表的工作原理有了更深入的理解。

观看《低俗小说》中的昆斯船长场景

如今,我仍然喜欢把事物拆开来更好地理解。我也鼓励你也这样做。

我们先从外观开始reduce。我一眼就能看出它四个array部分methodreducerinitialValue

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
*/
Enter fullscreen mode Exit fullscreen mode

一切都很容易理解。除了 之外的一切reducer。这需要进一步分析。

注意:Reducers 有 4 个参数,现在我们将忽略最后 2 个并专注于accumulatorcurrentValue

这些参数通常缩写为acccur

const reducer = (acc, cur) => acc + cur
Enter fullscreen mode Exit fullscreen mode

因为您已经熟悉 for 循环,所以我可以使用下面的 for 循环来帮助演示什么accumulatorcurrentValue和如何使用它们。

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
}
Enter fullscreen mode Exit fullscreen mode

并插入reducer...

for (let i = 0; i < items.length; i++) {
  const cur = items[i]
  acc = reducer(acc, cur)
}
Enter fullscreen mode Exit fullscreen mode

如果您想查看更多类似的细分,请查看Map、Filter、Reduce 与 For 循环(语法)

累加器

在上面的例子中,accumulatorNumber,但它不一定是Number,它可以是任何类型。

在这个例子中,acc是一个,Array并且reducer将一个双倍的值推入accumulator

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 ]
Enter fullscreen mode Exit fullscreen mode

在这个例子中,accumulator是一个对象,并且新值被添加到该对象中。

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 }
Enter fullscreen mode Exit fullscreen mode

你应该注意到,这两个例子中的 for 循环代码完全相同。不信?往回翻一翻看看!只有initialValuereducer改变了。所以,无论accumulator是 a Number、 an Array、 an Object,还是其他类型……你只需要修改initialValuereducer,而不用修改循环!

减少

因为我们知道 for 循环永远不会改变,所以很容易将其提取到它自己的函数中reduce

const reduce = () => {
  for (let i = 0; i < items.length; i++) {
    const cur = items[i]
    acc = reducer(acc, cur)
  }
}
Enter fullscreen mode Exit fullscreen mode

你的 linter 应该会报错缺失reduceritems所以我们来添加这些。我们还会添加一个initialValue

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
}
Enter fullscreen mode Exit fullscreen mode

就这样?我们刚刚创建了吗reduce?看起来太简单了!

嗯,我们确实忽略了 中的那两个额外参数reducer。另外,initialValueinreduce应该是可选的,但在我们的版本中是必需的。我们稍后会讨论这个问题。

地图

可以说map是 的导数reduce。在这种情况下,我们可以使用reducer上面的 ,将其传递给reduce,并提供 的初始值[]。初始值为 ,[]因为我们的结果将是Array

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 ]
Enter fullscreen mode Exit fullscreen mode

筛选

filter几乎与完全相同map。我们只需reducer根据结果将过滤值更改为predicate

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 ]
Enter fullscreen mode Exit fullscreen mode

其他功能

in应该是可选的。我们应该能够做到这一点并得到 的结果,但我们得到的initialValuereduce10NaN

const add = (acc, cur) => acc + cur

const items = [ 1, 2, 3, 4 ]

reduce(items, add) //=> NaN
Enter fullscreen mode Exit fullscreen mode

你会如何将其变为initialValue可选的?请在评论区展示你的代码。

我上面提到过,一个 Reducer 需要 4 个参数。这 4 个参数分别是:

  • 累加器(累加器)
  • 当前值(currentValue)
  • 当前索引(currentIndex)
  • 源数组(源)

我们已经实现了accumulatorcurrentValue。你会如何实现currentIndexsource?请在评论区展示你的代码。

额外积分

修改reduce为同时适用于ArrayIterator。这是Arrayreduce 无法做到的。

// 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)
Enter fullscreen mode Exit fullscreen mode

创建一个reduceWhile函数。它类似于reduce,但需要添加一个额外的函数,当满足给定条件时,该函数会中断迭代。可以将其视为breakfor 循环中的 。

const predicate = (acc, cur) => acc + cur < 7

const reduce = (items, predicate, reducer, initialValue) => {
  /* solution goes here */
}
Enter fullscreen mode Exit fullscreen mode

聚苯乙烯

本文以一种特定的方式对参数进行了排序,以便初学者更容易阅读。但如果我要将这些函数设计得更适合函数式编程 (FP),我会按如下方式对参数进行排序:

  • 谓词
  • 减速器
  • 初始值
  • 列表

概括

在解构map、、filterreduce了解其内在秘密之后,我们就能更容易地理解它们了。

显而易见,通过构建自己的reduce,您可以扩展一些功能,例如能够支持Iterator或 提前中断。我甚至进一步完善了MojiScript 的功能reduce,使其不仅支持 ,async Iterator还支持async reducer

您希望我更详细地讲解哪些内容?阅读本文后您学到了什么吗?请在评论区留言告诉我!

如果您喜欢函数式 JavaScript,请在​​这里或在 Twitter 上关注我@joelnet

干杯!

鏂囩珷鏉ユ簮锛�https://dev.to/joelnet/deconstructing-map-filter-and-reduce-1d1a
PREV
函数式编程:IF 的替代方案 #Functional #JavaScript #Functors 解决方案 A:ifVal 辅助函数 解决方案 B:Functors 结束示例一 示例二 示例三
NEXT
最适合编程的键盘 我用过的最适合编程的键盘