reduce

_.reduce(list, iteratee, [memo], [context])Aliases: inject, foldl
别名为 injectfoldl, reduce 方法把 list 中元素归结为一个单独的数值。Memo 是 reduce 函数的初始值,会被每一次成功调用 iteratee 函数的返回值所取代 。这个迭代传递4个参数:memo,value 和 迭代的 index(或者 key)和最后一个引用的整个 list

如果没有 memo 传递给 reduce 的初始调用,iteratee 不会被列表中的第一个元素调用。第一个元素将取代 memo 参数传递给列表中下一个元素调用的 iteratee 函数。

1
2
var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
=> 6

代码实现

reduce.js

1
2
3
4
import createReduce from './_createReduce.js';

// 从一组值中构建出一个结果
export default createReduce(1);

_createReduce.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import isArrayLike from './_isArrayLike.js';
import keys from './keys.js';
import optimizeCb from './_optimizeCb.js';
// 引入方法理解见 01 each

// 创建 reducing 函数的 内部方法, 向左或向右迭代.
export default function createReduce(dir) {
// 函数内部重新分配参数变量
var reducer = function(obj, iteratee, memo, initial) {
// 不是数组类数组,取对象键的集合
var _keys = !isArrayLike(obj) && keys(obj),
// object键集合的长度,或数组类数组的length属性
length = (_keys || obj).length,
// dir > 0 向右,<= 0 向左
index = dir > 0 ? 0 : length - 1;
// 当没有初始化的元素传入,memo就是每一项的结算结果
if (!initial) {
memo = obj[_keys ? _keys[index] : index];
index += dir;
}
// 起始项到当前项
for (; index >= 0 && index < length; index += dir) {
var currentKey = _keys ? _keys[index] : index;
// 当前项 和 前一次计算的结果作为新的 迭代器的参数,计算的结果等于 memo 的值
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};

// 接受参数,迭代元素,迭代器,初始化的元素,上下文
return function(obj, iteratee, memo, context) {
// 如果参数大于等于3,后面的参数作为计算的起始项
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
}

_optimizeCb.js

1
2
3
4
5
// 这里主要用到了 argCount = 4 的情况
case 4: return function(accumulator, value, index, collection) {
// 改变 this 指向,依次传入,累计计算结果,当前值,当前索引,迭代的集合
return func.call(context, accumulator, value, index, collection);
};

数组reduce方法实现

1
2
3
4
5
6
7
8
9
10
function reduce(arr,fn,initVal) {   
let initIndex
let acc
initIndex = arguments.length === 3 ? 1 : 0
acc = arguments.length === 3 ? initVal : arr[0]
for(let initIndex; i < arr.length; i++ ) {
acc = fn(acc, arr[i])
}
return acc
}

ReduceRight

1
2
3
4
5
import createReduce from './_createReduce.js';

// 从右侧开始的 reduce
export default createReduce(-1);