一、语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
_如果没有initialValue,则currentIndex从1开始
_
二、使用场景
1. 数组求和/求积
const arr = [2, 3, 6, 7]
// 求和,初始传0
const sum = arr.reduce((total, current) => {
return total + current
}, 0) // 18
// 求积,初始传1
const product = arr.reduce((total, current) => {
return total * current
}, 1) // 252
2. 数组扁平化
const arr1 = [1, 2, [2,3],[[4, 5]], 6]
const flatten = (arr) => {
return arr.reduce((total, current) => {
return total.concat(Array.isArray(current) ? flatten(current) : current)
}, [])
}
// 当然,es6 的flat方法也可以
const flatArr = arr.flat() // 但flat会移除数组中的空项
3. 计算数组中元素出现的次数
let arr = [a, b, c, a]
const countObj = arr.reduce((pre, cur) => {
if(cur in pre) {
pre[cur]++
} else {
pre[cur] = 1
}
return pre
}, {})
// countObj: {a:2,b:1,c:1}
4. 数组去重
let arr = [1,2,3,1]
let newArr = arr.reduce((pre, cur) => {
if(!pre.includes(cur)) {
return pre.concat(cur)
} else {
return pre
}
}, [])
5. 链式获取对象的属性值
const obj = {
a: 1,
b: {
c: {
d: 2
}
}
}
// 获取对象中的d值
const str = 'b.c.d'
const attrs = str.split('.')
const value = attrs.reduces((newObj, key) => newObj[key], obj) // 2
6. 根据属性把对象分类
const obj = [ {name: 'a', age: 15, gender: '男'}, {name: 'b', age: 16, gender: '男'}, {name: 'c', age: 15, gender: '女'},]
function groupBy(arr, property) {
return arr.reduce((resObj, curObj) = > {
const key = curObj[property]
if(!resObj[key]) {
resObj[key] = []
} else {
resObj[key].push(curObj)
}
return resObj
}, {})
}
const ageGroup = groupBy(obj, 'age')
// {15: [{name: 'a', age: 15, gender: '男'}, {name: 'c', age: 15, gender: '女'}], 16:{name: 'b', age: 16, gender: '男'}}
7.求最值
const arr = [12, 42, 12, 61, 73, 19]
const max = arr.reduce((prev, cur) => {
return prev > cur ? prev : cur
})
console.log(max) // 73
// 当然Math.max也能实现
// Math.max(...arr)
8. 类型转换
数组转对象
const goods = [{type: 'plate', No: 123}, {type: 'bowl', No: 321}]
const result = goods.reduce((prev, cur) => {
!prev[cur.No] && (prev[cur.No] = cur)
return prev
}, {})
console.log(result ) // {123: {type: "plate", No: 123}, 321: {type: "bowl", No: 321}}
对象转数组
const obj = {123: {type: "plate", No: 123}, 321: {type: "bowl", No: 321}}
const result = Object.keys(obj).reduce((prev, cur) => {
prev.push(obj[cur])
return prev
}, [])
console.log(result) // [{type: "plate", No: 123}, {type: "bowl", No: 321}}]
三、高级用法
1. 对象中选取对应于给定键的键值对pick
使用Array.reduce()将筛选/选取的密钥转换回具有相应键值对的对象 (如果在 obj 中存在该键)。
const pick = (obj, arr) =>
arr.reduce((acc, cur) => (cur in obj && (acc[cur] = obj[cur]), acc), {});
pick({ 'a': 1, 'b': '2', 'c': 3 }, ['a', 'c'])
// { 'a': 1, 'c': 3 }
2. 从数组中移除给定函数返回false的元素
使用Array.filter()查找返回 truthy 值的数组元素和Array.reduce()以使用Array.splice()删除元素。使用三参数 (func value, index, array调用函数)
const remove = (arr, func) =>
Array.isArray(arr) ?
arr.filter(func).reduce((acc, val) => {
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, [])
: [];
// remove([1, 2, 3, 4], n => n % 2 == 0) -> [2, 4]
3. 获取url参数
使用match()与适当的正则表达式来获取所有键值对,Array.reduce()可将它们映射并合并到单个对象中。将location.search作为要应用于当前url的参数传递.
const getURLParameters = url =>
url.match(/([^?=&]+)(=([^&]*))/g).reduce(
(a, v) => (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a), {}
);
getURLParameters('http://url.com/page?name=Adam&surname=Smith')
// {name: 'Adam', surname: 'Smith'}
4. 执行从右向左/从左向右的函数组合。
从右向左
使用Array.reduce()执行从右向左的函数组合。最后一个 (最右边) 的函数可以接受一个或多个参数;其余的函数必须是一元的。
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const add5 = x => x + 5
const multiply = (x, y) => x * y
const multiplyAndAdd5 = compose(add5, multiply)
multiplyAndAdd5(5, 2)
// 15
从左向右
使用Array.reduce()与扩展运算符 (...) 执行从左向右的函数组合。第一个 (最左边的) 函数可以接受一个或多个参数;其余的函数必须是一元的。
const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
const add5 = x => x + 5
const multiply = (x, y) => x * y
const multiplyAndAdd5 = pipeFunctions(multiply, add5)
multiplyAndAdd5(5, 2)
// 15
5. 按顺序运行多个promise
const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = (d) => new Promise(r => setTimeout(r, d))
const a = () => delay(1000)
const b = () => delay(2000)
runPromisesInSeries([a, b])
// executes each promise sequentially, taking a total of 3 seconds to complete
6. 生成一个数组, 包含斐波那契数列
创建一个指定长度的空数组, 初始化前两个值 (0和1)。使用Array.reduce()可将值添加到数组中, 方法是使用前两个值的总和, 但前两个数值除外。
const fibonacci = n =>
Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []);
fibonacci(5)
// [0,1,1,2,3]
7. 从给定的键值对创建对象。
const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
objectFromPairs([['a',1],['b',2]])
// {a: 1, b: 2}
8. 生成字符串的所有字谜
使用递归。对于给定字符串中的每个字母, 为其其余字母创建所有部分字谜。使用Array.map()将字母与每个部分变位词组合在一起, 然后将Array.reduce()组合在一个数组中的所有字谜。基本情况为字符串length等于2或1.
const anagrams = str => {
if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
return str.split('').reduce((acc, letter, i) =>
acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), []);
};
anagrams('abc')
// ['abc','acb','bac','bca','cab','cba']