JS数组常用方法之reduce
reduce(回调,初始值?)
回调参数:累加器,当前值,当前索引,数组
1. 使用注意:
- 对数组中的每一项执行回调(若传入初始值,则第一项不执行回调,直接赋值给累加器)
- 若未传初始值,则第一个值不执行回调。首次累加器为数组第一项,当前值为第二项,当前索引为1。
- 若传入初始值,则首次累加器为数组第一项,当前值为第二项,当前索引为0
- 提供初始值通常更安全。
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
// reduce() 没有初始值
[ { x: 2 }, { x: 22 }, { x: 42 } ].reduce( maxCallback ); // NaN
[ { x: 2 }, { x: 22 } ].reduce( maxCallback ); // 22
[ { x: 2 } ].reduce( maxCallback ); // { x: 2 }
[ ].reduce( maxCallback ); // TypeError
var maxCallback2 = ( max, cur ) => Math.max( max, cur );
// map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
2. 案例:
(1)数组求和
[1, 2, 3].reduce((acc, cur) => acc + cur, 0) // 无初始值时,传入初始值为0,结果为6
[1, 2, 3].reduce((acc, cur) => acc + cur, 1) // 初始值为1时,结果为7
(2)对象数组的值求和
[ { x: 1 }, { x: 2 }, { x: 3 } ].reduce(() => acc + cur.x, 0) // 6
(3)数组去重
// 数组元素类型为 string
let arr = ['a', 'b', 'a', 'c', 'b', 'c'];
let orderArr = arr.reduce((acc, cur) => {
if(acc.indexOf(cur) === -1) {
acc.push(cur)
}
return acc
}, []) // ['a', 'b', 'c']
// 数组元素类型为 string
let arr = [1, 2, 1, 3, 2, 1];
let orderArr = arr.sort().reduce((acc, cur) => {
if(acc.length === 0 || acc[acc.length - 1] !== cur) {
acc.push(cur)
}
return acc
}, []) // [1, 2, 3]
(4)数组扁平化(二维降为一维)
[[1, 2], [3, 4], [5, 6]].reduce((acc, cur) => acc.concat(cur), []) // [1, 2, 3, 4, 5, 6]
延展:flat方法:指定深度遍历数组(默认为1,可不传),并返回合并后的新数组。
[1, 2, [3, 4]].flat(); // [1, 2, 3, 4]
[1, 2, [[[3, 4]]]].flat(2); // [1, 2, [3, 4]]
(5)数组元素计数
var num = ['a', 'b', 'a'].reduce((acc, cur) => {
if(cur in acc) {
acc[cur]++;
} else {
acc[cur] = 1;
}
return acc;
}, {}) // {a: 2, b: 1}
(6)属性分类
const people = [{name: 'zs', age: 18}, {name: 'ls', age: 18}, {name: 'ww', age: 20}];
const getGroup = (prop) => people.reduce((acc, cur) => {
let val = cur[prop];
if(!acc[val]) {
acc[val] = []
}
acc[val].push(cur);
return acc;
}, {});
console.log(getGroup('age'))
{
20: [{name: 'zs', age: 18}, {name: 'ls', age: 18}],
21: [{name: 'ww', age: 20}]
}
(7)提取和合并对象数组中的数组
const people = [{ name: 'zs', age: 18, hobbies: ['singing', 'dancing']
},
{
name: 'ls',
age: 18,
hobbies: ['reading']
},
{
name: 'ww',
age: 20,
hobbies: ['running', 'playing basketball']
}
];
const allHobbies = people.reduce((acc, cur) => [...acc, ...cur.hobbies], ['travelling']);
// allHobbies: ['travelling', 'singing', 'dancing', 'reading', 'running', 'playing basketball']
(8)数组转换为对象
// 使用 reduce
const people = [{name: 'zs', age: 18},
{name: 'ls', age: 18},
{name: 'ww', age: 20}];
const newObj = people.reduce((acc, cur) => {
acc[cur.name] = cur.age;
return acc
}, {});
// reduce中的回调可以简化:
// const newObj = people.reduce((acc, cur) => (acc[cur.name] = cur.age, acc), {});
// 该形式利用了逗号操作符:对括号里的每一项求值,返回最后一项
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Comma_Operator
console.log(newObj)
// {zs: 18, ls: 18, ww: 20}
// 使用 Object.fromEntries
const people = [{name: 'zs', age: 18},
{name: 'ls', age: 18},
{name: 'ww', age: 20}];
const newObj = Object.fromEntries(
people.map(({name, age}) => [name, age])
)
console.log(newObj)
// {zs: 18, ls: 18, ww: 20}
(9)按顺序运行Promise
const f1 = val => new Promise((res, ret) => res(val * 10))
const f2 = val => new Promise((res, ret) => res(val * 20))
const f3 = val => new Promise((res, ret) => res(val * 30))
const promiseArr = [f1, f2, f3]
promiseArr.reduce((acc, cur) => acc.then(cur), Promise.resolve(5)).then(console.log)
// 30000
(10)功能型函数管道
const double = x => x * 2
const triple = x => x * 3
const quadruple = x => x * 4
const pipe = (...functions) => initVal => functions.reduce((acc, cur) => cur(acc), initVal)
const mul_6 = pipe(double, triple)
const mul_12 = pipe(triple, quadruple)
console.log(mul_6(1)) // 6
console.log(mul_6(2)) // 12
console.log(mul_12(1)) // 12
console.log(mul_12(2)) // 24
参考文献:
1.developer.mozilla.org/z/docs/Web/… 2.segmentfault.com/q/101000004…
转载自:https://juejin.cn/post/7051955650628157447