reduce
reduce作为es5新增的常规数组方法之一,却比forEach,filter和map更强大。
- 它是对数组中的每个元素执行一个自定义的累计器,将结果汇总为单个返回值。
- 形式:
array.reduce((t, v, i, a) => {}, initValue)
- 参数
- callback:回调函数(
必选
)
- initValue:初始值(
可选
)
- 回调函数的参数
- total(
t
):累计器完成计算的返回值(必选
)
- value(
v
):当前元素(必选
)
- index(
i
):当前元素的索引(可选
)
- array(
a
):当前元素所属的数组对象(可选
)
- 过程
- 以
t
作为累计结果的初始值,不设置t
则以数组第一个元素为初始值
- 开始遍历,使用累计器处理
v
,将v
的映射结果累计到t
上,结束此次循环,返回t
- 进入下一次循环,重复上述操作,直至数组最后一个元素
- 结束遍历,返回最终的
t
reduce
的精华所在是将累计器逐个作用于数组成员上,把上一次输出的值作为下一次输入的值。下面举个简单的例子,看看reduce
的计算结果。
1 2 3 4
| const arr = [3, 5, 1, 4, 2]; const a = arr.reduce((t, v) => t + v);
const b = arr.reduce((t, v) => t + v, 0);
|
贴一个reduce
的作用动图

reduce
实质上是一个累计器函数,通过用户自定义的累计器对数组成员进行自定义累计,得出一个由累计器生成的值。另外reduce
还有一个胞弟reduceRight
,两个方法的功能其实是一样的,只不过reduce
是升序执行,reduceRight
是降序执行。
对空数组调用reduce()和reduceRight()是不会执行其回调函数的,可认为reduce()对空数组无效
高级用法
累加累乘
1 2 3 4 5 6 7 8 9 10 11
| function Accumulation(...vals){ return vals.reduce((total,value) => { total + value },0); }
function Multiplication(...vals){ return vals.reduce((total,value) => { total * value },1); }
|
权重求和
1 2 3 4 5 6
| const scores = [ { score: 90, subject: "chinese", weight: 0.5 }, { score: 95, subject: "math", weight: 0.3 }, { score: 85, subject: "english", weight: 0.2 } ]; const result = scores.reduce((t, v) => t + v.score * v.weight, 0);
|
代替reverse
1 2 3 4 5
| function Reverse(arr = []) { return arr.reduceRight((t, v) => (t.push(v), t), []); }
Reverse([1, 2, 3, 4, 5]);
|
(t.push(v), t),逗号最后面的t 代表的啥意思?
这是用到了逗号操作符,返回t的意思
代替map和filter
1 2 3 4 5 6 7 8 9 10 11 12 13
| const arr = [0,1,2,3];
const a = arr.map(v => v * 2); const b = arr.reduce((t, v) => [...t, v * 2], []);
const c = arr.filter(v => v > 1); const d = arr.reduce((t, v) => v > 1 ? [...t, v] : t, []);
const e = arr.map(v => v * 2).filter(v => v > 2); const f = arr.reduce((t, v) => v * 2 > 2 ? [...t, v * 2] : t, []);
|
代替some和every
1 2 3 4 5 6 7 8 9 10 11
| const scores = [ { score: 45, subject: "chinese" }, { score: 90, subject: "math" }, { score: 60, subject: "english" } ];
const isAtLeastOneQualified = scores.reduce((t, v) => t || v.score >= 60, false);
const isAllQualified = scores.reduce((t, v) => t && v.score >= 60, true);
|
数组分割
1 2 3 4 5 6
| function Chunk(arr = [], size = 1) { return arr.length ? arr.reduce((t, v) => (t[t.length - 1].length === size ? t.push([v]) : t[t.length - 1].push(v), t), [[]]) : []; } const arr = [1, 2, 3, 4, 5]; Chunk(arr, 2);
|
数组过滤
1 2 3 4 5 6 7
| function Difference(arr = [], oarr = []) { return arr.reduce((t, v) => (!oarr.includes(v) && t.push(v), t), []); } const arr1 = [1, 2, 3, 4, 5]; const arr2 = [2, 3, 6] Difference(arr1, arr2);
|
数组填充
1 2 3 4 5 6 7 8 9 10 11
| function Fill(arr = [], val = "", start = 0, end = arr.length) { if (start < 0 || start >= end || end > arr.length) return arr; return [ ...arr.slice(0, start), ...arr.slice(start, end).reduce((t, v) => (t.push(val || v), t), []), ...arr.slice(end, arr.length) ]; } const arr = [0, 1, 2, 3, 4, 5, 6]; Fill(arr, "aaa", 2, 5);
|
数组扁平
1 2 3 4 5 6
| function Flat(arr = []) { return arr.reduce((t, v) => t.concat(Array.isArray(v) ? Flat(v) : v), []) } const arr = [0, 1, [2, 3], [4, 5, [6, 7]], [8, [9, 10, [11, 12]]]]; Flat(arr);
|
数组去重
1 2 3 4 5 6
| function Uniq(arr = []) { return arr.reduce((t, v) => t.includes(v) ? t : [...t, v], []); } const arr = [2, 1, 0, 3, 2, 1, 2]; Uniq(arr);
|
数组最大最小值
1 2 3 4 5 6 7 8 9 10 11
| function Max(arr = []) { return arr.reduce((t, v) => t > v ? t : v); }
function Min(arr = []) { return arr.reduce((t, v) => t < v ? t : v); } const arr = [12, 45, 21, 65, 38, 76, 108, 43]; Max(arr); Min(arr);
|
数组成员独立拆解
1 2 3 4 5 6 7 8 9
| function Unzip(arr = []) { return arr.reduce( (t, v) => (v.forEach((w, i) => t[i].push(w)), t), Array.from({ length: Math.max(...arr.map(v => v.length)) }).map(v => []) ); } js const arr = [["a", 1, true], ["b", 2, false]]; Unzip(arr);
|
数组成员个数统计
1 2 3 4 5 6
| function Count(arr = []) { return arr.reduce((t, v) => (t[v] = (t[v] || 0) + 1, t), {}); }
const arr = [0, 1, 1, 2, 2, 2]; Count(arr);
|
此方法是字符统计和单词统计的原理,入
参时把字符串处理成数组即可
数组成员位置记录
1 2 3 4 5
| function Position(arr = [], val) { return arr.reduce((t, v, i) => (v === val && t.push(i), t), []); } const arr = [2, 1, 5, 4, 2, 1, 6, 6, 7]; Position(arr, 2);
|
数组成员特性分组
1 2 3 4 5 6 7 8 9 10 11 12
| function Group(arr = [], key) { return key ? arr.reduce((t, v) => (!t[v[key]] && (t[v[key]] = []), t[v[key]].push(v), t), {}) : {}; } 复制代码 const arr = [ { area: "GZ", name: "YZW", age: 27 }, { area: "GZ", name: "TYJ", age: 25 }, { area: "SZ", name: "AAA", age: 23 }, { area: "FS", name: "BBB", age: 21 }, { area: "SZ", name: "CCC", age: 19 } ]; Group(arr, "area");
|
数组成员所含关键字统计
1 2 3 4 5 6 7 8 9 10 11
| function Keyword(arr = [], keys = []) { return keys.reduce((t, v) => (arr.some(w => w.includes(v)) && t.push(v), t), []); } const text = [ "今天天气真好,我想出去钓鱼", "我一边看电视,一边写作业", "小明喜欢同桌的小红,又喜欢后桌的小君,真TM花心", "最近上班喜欢摸鱼的人实在太多了,代码不好好写,在想入非非" ]; const keyword = ["偷懒", "喜欢", "睡觉", "摸鱼", "真好", "一边", "明天"]; Keyword(text, keyword);
|
字符串翻转
1 2 3 4 5
| function ReverseStr(str = "") { return str.split("").reduceRight((t, v) => t + v); } const str = "reduce最牛逼"; ReverseStr(str);
|
数字千分化
1 2 3 4 5 6 7 8 9 10 11
| function ThousandNum(num = 0) { const str = (+num).toString().split("."); const int = nums => nums.split("").reverse().reduceRight((t, v, i) => t + (i % 3 ? v : `${v},`), "").replace(/^,|,$/g, ""); const dec = nums => nums.split("").reduce((t, v, i) => t + ((i + 1) % 3 ? v : `${v},`), "").replace(/^,|,$/g, ""); return str.length > 1 ? `${int(str[0])}.${dec(str[1])}` : int(str[0]); }
ThousandNum(1234); ThousandNum(1234.00); ThousandNum(0.1234); ThousandNum(1234.5678);
|
异步累计
1 2 3 4 5 6 7 8 9
| async function AsyncTotal(arr = []) { return arr.reduce(async(t, v) => { const at = await t; const todo = await Todo(v); at[v] = todo; return at; }, Promise.resolve({})); } const result = await AsyncTotal();
|
斐波那契数列
1 2 3 4 5 6
| function Fibonacci(len = 2) { const arr = [...new Array(len).keys()]; return arr.reduce((t, v, i) => (i > 1 && t.push(t[i - 1] + t[i - 2]), t), [0, 1]); }
Fibonacci(10);
|
URL参数反序列化
1 2 3 4 5 6 7 8 9 10
| function ParseUrlSearch() { return location.search.replace(/(^\?)|(&$)/g, "").split("&").reduce((t, v) => { const [key, val] = v.split("="); t[key] = decodeURIComponent(val); return t; }, {}); }
ParseUrlSearch();
|
URL参数序列化
1 2 3 4 5 6 7
| function StringifyUrlSearch(search = {}) { return Object.entries(search).reduce( (t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`, Object.keys(search).length ? "?" : "" ).replace(/&$/, ""); } StringifyUrlSearch({ age: 27, name: "YZW" });
|
返回对象指定键值
1 2 3 4 5 6 7
| function GetKeys(obj = {}, keys = []) { return Object.keys(obj).reduce((t, v) => (keys.includes(v) && (t[v] = obj[v]), t), {}); } 复制代码 const target = { a: 1, b: 2, c: 3, d: 4 }; const keyword = ["a", "d"]; GetKeys(target, keyword);
|
数组转对象
1 2 3 4 5 6 7 8 9
| const people = [ { area: "GZ", name: "YZW", age: 27 }, { area: "SZ", name: "TYJ", age: 25 } ]; const map = people.reduce((t, v) => { const { name, ...rest } = v; t[name] = rest; return t; }, {});
|
Redux Compose函数原理
1 2 3 4 5 6 7 8 9
| function Compose(...funs) { if (funs.length === 0) { return arg => arg; } if (funs.length === 1) { return funs[0]; } return funs.reduce((t, v) => (...arg) => t(v(...arg))); }
|
###