Javascripts之数组的reduce

目录

研究一下 javascripts 的 reduce ,reduce 是既能改变 array 的 size,又能改变数值的函数,filter 是只能改变size,不能改变数值;而 map 是不能改变 size,可以改变数值。很拗口吧,三个兄弟。

简单介绍一下 reduce。

假设我们有一个数组:

1[1, 2, 3, 4]

我们要对整个数组求和.

reduce 实际是按照下列的算式来进行求和的:

((((1) + 2) + 3) + 4)

那实际 reduce 函数执行中,你可以按你需求来自定义你自己的 + 操作符。数组的值也可以是其他的任意东西。听起来有点意思吧?

1、 Reduce 是干嘛的

在一个函数式编程语言中,reduce 其实有很多别的名称,比如 fold(对折), accumulate(累加器), aggregate(聚合器), compress(压缩) 甚至叫 inject(注入)

2、 Reduce 的参数

常用用法如下:

1let myArray      = [/* 首先定义一个数组 */];
2let callbackfn   = /* 再定义一个函数 */ ;
3let initialvalue = /* 任意一个初始化的值 */ ;
4
5myArray.reduce(callbackfn)
6myArray.reduce(callbackfn, initialValue)

reduce 的参数如下:

callbackfn: 必须是一个函数,会在整个数组中反复调用,reduce 调用 callbackfn 的时候有4个参数,我们定义它们是 previousValue, currentElement, indexarray ,看起来像下面一样:

1callbackfn(previousValue, currentElement, index, array)

解释一下:

  1. previousValue: 这个参数就是一个累加器。
  2. currentElement: 数组中处理的当前元素。
  3. index: 当前元素的索引值。
  4. array: myArray调用的数组.

Return value(返回值): 最后一次调用 callbackfn 的时候,返回值就是整个 reduce 过程的返回值。如果不是最后一次调用,它返回的值会被下次的 callbackFnpreviousvalue 参数接收。

我们必须注意,函数就是函数,函数过程中处理的变量要么是外围的 scope 带进来的,要么是函数体内自定义的,所以后面两个 index 和 array 也是必须存在的,只不过是 reduce 函数替你自动处理了。

3、 用画图来理解 Reduce

image-20220521150951876

看上面的图,reduce 和 reductRight 函数的区别就是方向,一个是从左到右,一个是从右到左。

关注点如下:

  • acc 相当于 previousValue ,累加器.
  • curVal 相当于currentElement,当前处理元素.
  • 数组中的每个元素向下输出到圈 r 就是curVal输出到***r***的具体表现.
  • 包含数组元素的长方形输出到下一个 r 就是 acc 输出到***r***的具体表现.
  • 初始化值在数组外单独表示,它是作为一个单独的 acc 输出到 r 中的.

3、 用流程图来理解 Reduce

下面用20行的伪代码来详细解释整个 reduce 的过程,首先进入 reduce 函数:

  1. If initialValue is present, 如果初始化变量不为空
  2.    If myArray has no elements, 接着判断如果数组为空
  3.       Return initialValue. 那么直接初始化变量作为 reduce 的结果返回
  4.   else 初始化变量为空但数组不为空
  5.     Let accumulator be initialValue. 把初始变量赋给累加器
  6.     If the method is reduce, 如果方法是 reduce
  7.       Let startIndex be the index of the leftmost element of myArray. 把数组最左边的元素的index值赋予 startIndex
  8. else 如果初始化变量为空
  9.   If myArray has no elements, 如果数组没有元素
  10.     Throw TypeError. 初始化变量为空,数组也为空,直接抛类型错误
  11.   Else if myArray has just only one element, 如果数组只有一个元素
  12.     Return that element. 那么直接将数组中唯一一个元素作为 reduce 结果返回
  13.   Else
  14.     If the method is reduce,如果方法是 reduce
  15.       Let accumulator be the leftmost element of myArray. 把数组最左边的第一个元素赋给累加器
  16. If the method is reduce, 如果方法是reduce
  17.   In left to right order, for each element of myArray such that its index istartingIndex, 按照从左到右的顺序,遍历数组中的每一个元素,来个大循环
  18.     Set accumulator to callbackfn(accumulator, myArray[i], i, myArray). 数组中的每个元素,都逐个设置到callbackfn函数中并运行
  19. Return accumulator. 累加器的值作为 reduce 结果返回

仔细理解,搞完了吧。

给个实际例子,一群学生,有男有女,先选出女学生,然后计算出她们每个人的平均成绩,最后把她们打印出来:

 1const students = [ 
 2  { 
 3    name: "Anna", 
 4    sex: "f", 
 5    grades: [4.5, 3.5, 4] 
 6  }, 
 7
 8  { 
 9    name: "Dennis", 
10    sex: "m", 
11    country: "Germany", 
12    grades: [5, 1.5, 4] 
13  }, 
14
15  { 
16    name: "Martha", 
17    sex: "f", 
18    grades: [5, 4, 2.5, 3] 
19  }, 
20
21  { 
22    name: "Brock", 
23    sex: "m", 
24    grades: [4, 3, 2] 
25  } 
26]; 
27
28   
29//TODO: Compute and Return female students results using functional programming. 
30
31function studentResult(students){ 
32  return students.filter(x => x.sex=="f").reduce((init,cur)=>{ 
33    cur['grades']=cur.grades.reduce((acc,cur) => acc+cur,0)/cur.grades.length; 
34    return init.concat(cur);
35  },[]); 
36} 
37
38console.log(studentResult(students));
39//[ { name: 'Anna', sex: 'f', grades: 4 },   { name: 'Martha', sex: 'f', grades: 3.625 } ] 
40

注意一点,reduce 可以动 size,也可以动值,上面实际改变了 students 数组元素的值了,不太好。应该赋一个新值 concat 或者 push 进新数组。

下面的方法就没有动原数组的数据:

 1function studentResult(students){ 
 2  return students.filter(x => x.sex=="f").reduce((init,cur)=>{ 
 3    let newarr = {}; 
 4    newarr['name']=cur.name; 
 5    newarr['sex']=cur.sex; 
 6    newarr['grades']=cur.grades.reduce((acc,cur) => acc+cur,0)/cur.grades.length; 
 7    init.push(newarr); 
 8    return init; 
 9  },[]); 
10} 

面试之Docker如何打出最小的镜像
Javascripts中的module
comments powered by Disqus