文章目录
定义和用法
reduce是数组内置的一个方法,原型链上位于Array.prototype.reduce()
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。
reduce对数组累积执行回调函数,返回最终计算结果
语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
- function(total, currentValue, currentIndex, arr)
参数 | 描述 |
---|---|
total | 必需。初始值, 或者计算结束后的返回值。 |
currentValue | 必需。当前元素 |
currentIndex | 可选。当前元素的索引 |
arr | 可选。当前元素所属的数组对象。 |
- initialValue
可选。传递给函数的初始值
示例
1、求数组中所有元素的值的和
const arr01 = [10,20,30]
const result01 = arr01.reduce((total, currentValue)=>{
return total + currentValue
}, 0)
console.log(result01)
2、求对象数组中指定属性值的和
const arr02 = [
{x:10}, {x:20}, {x:30}
]
const result02 = arr02.reduce((total, currentValue)=>{
return total + currentValue.x
}, 0)
console.log(result02)
3、将二维数组中的每个子数组元素进行合并,即二维数组扁平化
const arr03 = [
[10, 20, 30, 40],
[10, 20, 30, 40],
[10, 20, 30, 40],
[10, 20, 30, 40]
]
const result03 = arr03.reduce((total, currentValue) => {
return total.concat(currentValue)
}, [])
console.log(result03)
4、统计数组元素的频数
将数组中每个元素(重复)出现的次数进行统计,最后将结果以对象的形式输出。
const arr04 = [
"a", "b", "a", "b", "a", "b", "a", "b", "a", "a", "a", "a", "a", "b", "b", "b",
"c", "c", "c", "c", "d", "d", "d", "e", "e", "e",
]
const result04 = arr04.reduce(
(counterObj, currentValue) => {
if(currentValue in counterObj){
counterObj[currentValue] ++;
}else{
counterObj[currentValue] = 1;
}
return counterObj;
},
{}
)
console.log(result04)
5、分组
根据指定属性,对对象数组中的对象进行分组。
const arr05 = [
{ 年级: "2017级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2017级", 学院: "计算机学院", 专业: "计算机科学" },
{ 年级: "2018级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2019级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2020级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2020级", 学院: "计算机学院", 专业: "计算机科学" },
{ 年级: "2017级", 学院: "电气学院", 专业: "自动化" },
{ 年级: "2018级", 学院: "电气学院", 专业: "自动化" },
{ 年级: "2019级", 学院: "电气学院", 专业: "自动化" },
{ 年级: "2020级", 学院: "电气学院", 专业: "自动化" },
]
// 根据指定字段的指定值获取分组 例如: 按照年级分组
// target 是"年级" targetValue是"2017级别"
function getByTarget(arr, target, targetValue) {
// 例如:如果不指定年级,则返回全部分组
if (!targetValue) {
const result = arr.reduce(
(accumulator, currentObj) => {
// 例如 currentTargetValue = "2017级" target = "年级"
const currentTargetValue = currentObj[target]
// 例如:当前的年级值是否在accumulator中
if (!accumulator[currentTargetValue]) {
accumulator[currentTargetValue] = []
}
accumulator[currentTargetValue].push(currentObj)
return accumulator;
},
{}
)
return result;
}
// 例如:如果指定了年级
let initObj = {}
initObj[targetValue] = [];
const result = arr.reduce(
(accumulator, currentObj) => {
// 不是目标值直接跳过
if (currentObj[target] !== targetValue) {
return accumulator;
}
// 符合要求则加入
accumulator[targetValue].push(currentObj);
return accumulator;
},
initObj
)
return result;
}
result05 = getByTarget(arr05, "专业")
// 这里是为了确保函数返回值是一个map而不是数组(统一返回格式) 重在思路
result05_01 = getByTarget(arr05, "年级", "2017级")["2017级"]
result05_02 = getByTarget(result05_01, "学院", "计算机学院")["计算机学院"]
// 例如:我想获得2017级计算机学院中有什么专业
console.log(result05)
console.log(result05_01)
console.log(result05_02)
6、结合"…"使用
const result06 = arr06.reduce(
(accumulator, currentObj) => {
return [...accumulator, ...currentObj["tip"]]
},
[]
)
console.log(result06)
7、数组元素的去重
const arr07 = [
"a", "b", "a", "b", "a", "b", "a", "b", "a", "a", "a", "a", "a", "b", "b", "b",
"c", "c", "c", "c", "d", "d", "d", "e", "e", "e",
]
const result07 = arr07.reduce(
(accumulator, currentValue)=>{
// 不存在则放入
if(accumulator.indexOf(currentValue) === -1){
accumulator.push(currentValue);
}
return accumulator;
},
[]
)
console.log(result07)
8、遍历数组,同时对符合条件的数组元素进行操作。
const arr08 = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 5, 6, 9, 8, 7, 4, 1, 2, 6, 3, 9, 5, 7
]
const result08 = arr08.reduce(
(accumulator, currentValue) => {
if (currentValue >= 5) {
currentValue *= 10;
accumulator.push(currentValue);
}
return accumulator;
},
[]
)
console.log(result08)
console.log(arr08)
9、实现promise队列
Promise.all().then()的用法,all()表示所有的操作均执行完成,再执行then()中的方法。而all()中的函数,不是顺序执行,而是同时执行。如果希望then()之前的函数都是顺序执行的,可以考虑通过reduce函数来实现。
这就实现了一个promise队列
console.log("----------------------------------------")
const arr09 = [
() => new Promise(
(resolve) => {
setTimeout(() => {
console.log(10);
resolve(10);
}, 1000)
}
).then(res => { console.log(res) }),
() => new Promise(
(resolve) => {
setTimeout(() => {
console.log(20);
resolve(20);
}, 1000)
}
).then(res => { console.log(res) }),
() => new Promise(
(resolve) => {
setTimeout(() => {
console.log(30);
resolve(30);
}, 1000)
}
).then(res => { console.log(res) }),
() => (40)
]
const result09 = arr09.reduce(
(prev, next) => prev.then(next),
// 这里是为了new一个promise形成链式调用
Promise.resolve()
)
console.log(result09)
const createPromise = (id) => () =>
new Promise(resolve=>
setTimeout(()=>{
console.log("promise->"+id+":"+new Date());
resolve();
},1000)
)
var tasks = [createPromise(1),createPromise(2),createPromise(3)];
console.log(tasks);
var doTask = tasks.reduce((prev,next)=>prev.then(()=>next()),Promise.resolve());
doTask.then(()=>console.log("all done."));
其他写法
[1, 2, 3, 4, 5].reduce((pre, cur) => {
return pre.then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(cur)
resolve()
}, 1000)
})
})
}, Promise.resolve())
10、实现函数管道
const addOne = x => x + 1;
const addTwo = x => x + 2;
const pipe = (input) => (...functions) => functions.reduce(
(accumulator, currentFunc) => {
input = currentFunc(accumulator);
return input;
},
input
)
let initVal = 10;
let finalVal = pipe(val)(addOne, addTwo)
console.log(initVal);
console.log(finalVal)
仔细一看,有函数柯里化那味了。
const pipe = (...args) => (input) => {
console.log(input);
console.log(args);
};
let result = pipe(1,2,3,4,5,6,7)(1)
console.log(result)
11、用reduce实现map函数
map相关概念
遍历数组的每一项,并执行回调函数的操作,返回一个对每一项进行操作后的新数组,
arr = []
arr.map(function(item,idx,arr){
item //数组的每一项
idx // 当前项的索引
arr // 当前遍历的数组
this // 函数体内的 this 为 callbackThis参数,
// 如果不指定或者指定为 null和 undefined,则 this指向全局对象
},callbackThis)
实现
Array.prototype.myMap = function(func, callbackThis){
// 最终返回新的数组
let res = [];
// 定义回调函数的执行环境
// call 第2个参数传入null 则this指向全部对象
let cbThis = callbackThis || null;
this.reduce(
(prev, next, index, arr)=>{
// 传入map回调函数拥有的参数
// 把每一项的执行结果push进入res
res.push(func.call(cbThis, next, index, arr));
},
null);
return res;
}
let arr11 = [1,2,3,4,5,6,7,8]
let result11 = arr11.myMap(Math.sqrt)
console.log(result11)
参考资料
完整示例代码
const arr01 = [10, 20, 30]
const result01 = arr01.reduce((total, currentValue) => {
return total + currentValue
}, 0)
console.log(result01)
// ---------------------------------------------------
const arr02 = [
{ x: 10 }, { x: 20 }, { x: 30 }
]
const result02 = arr02.reduce((total, currentValue) => {
return total + currentValue.x
}, 0)
console.log(result02)
// ---------------------------------------------------
const arr03 = [
[10, 20, 30, 40],
[10, 20, 30, 40],
[10, 20, 30, 40],
[10, 20, 30, 40]
]
const result03 = arr03.reduce((total, currentValue) => {
return total.concat(currentValue)
}, [])
console.log(result03)
// --------------------------------------------------
const arr04 = [
"a", "b", "a", "b", "a", "b", "a", "b", "a", "a", "a", "a", "a", "b", "b", "b",
"c", "c", "c", "c", "d", "d", "d", "e", "e", "e",
]
const result04 = arr04.reduce(
(counterObj, currentValue) => {
if (currentValue in counterObj) {
counterObj[currentValue]++;
} else {
counterObj[currentValue] = 1;
}
return counterObj;
},
{}
)
console.log(result04)
// -------------------------------------------------------
const arr05 = [
{ 年级: "2017级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2017级", 学院: "计算机学院", 专业: "计算机科学" },
{ 年级: "2018级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2019级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2020级", 学院: "计算机学院", 专业: "物联网" },
{ 年级: "2020级", 学院: "计算机学院", 专业: "计算机科学" },
{ 年级: "2017级", 学院: "电气学院", 专业: "自动化" },
{ 年级: "2018级", 学院: "电气学院", 专业: "自动化" },
{ 年级: "2019级", 学院: "电气学院", 专业: "自动化" },
{ 年级: "2020级", 学院: "电气学院", 专业: "自动化" },
]
// 根据指定字段的指定值获取分组 例如: 按照年级分组
// target 是"年级" targetValue是"2017级别"
function getByTarget(arr, target, targetValue) {
// 例如:如果不指定年级,则返回全部分组
if (!targetValue) {
const result = arr.reduce(
(accumulator, currentObj) => {
// 例如 currentTargetValue = "2017级" target = "年级"
const currentTargetValue = currentObj[target]
// 例如:当前的年级值是否在accumulator中
if (!accumulator[currentTargetValue]) {
accumulator[currentTargetValue] = []
}
accumulator[currentTargetValue].push(currentObj)
return accumulator;
},
{}
)
return result;
}
// 例如:如果指定了年级
let initObj = {}
initObj[targetValue] = [];
const result = arr.reduce(
(accumulator, currentObj) => {
// 不是目标值直接跳过
if (currentObj[target] !== targetValue) {
return accumulator;
}
// 符合要求则加入
accumulator[targetValue].push(currentObj);
return accumulator;
},
initObj
)
return result;
}
result05 = getByTarget(arr05, "专业")
// 这里是为了确保函数返回值是一个map而不是数组(统一返回格式) 重在思路
result05_01 = getByTarget(arr05, "年级", "2017级")["2017级"]
result05_02 = getByTarget(result05_01, "学院", "计算机学院")["计算机学院"]
// 例如:我想获得2017级计算机学院中有什么专业
console.log(result05)
console.log(result05_01)
console.log(result05_02)
// -------------------------------------------------------
const arr06 = [
{
name: "user01",
tip: ["流行", "日语", "动漫"]
},
{
name: "user02",
tip: ["怀旧", "粤语", "经典"]
},
{
name: "user03",
tip: ["现代", "韩语", "电视剧"]
}
]
const result06 = arr06.reduce(
(accumulator, currentObj) => {
return [...accumulator, ...currentObj["tip"]]
},
[]
)
console.log(result06)
// -------------------------------------------------------
const arr07 = [
"a", "b", "a", "b", "a", "b", "a", "b", "a", "a", "a", "a", "a", "b", "b", "b",
"c", "c", "c", "c", "d", "d", "d", "e", "e", "e",
]
const result07 = arr07.reduce(
(accumulator, currentValue) => {
// 不存在则放入
if (accumulator.indexOf(currentValue) === -1) {
accumulator.push(currentValue);
}
return accumulator;
},
[]
)
console.log(result07)
// -----------------------------------------------------
const arr08 = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 5, 6, 9, 8, 7, 4, 1, 2, 6, 3, 9, 5, 7
]
const result08 = arr08.reduce(
(accumulator, currentValue) => {
if (currentValue >= 5) {
currentValue *= 10;
accumulator.push(currentValue);
}
return accumulator;
},
[]
)
console.log(result08)
console.log(arr08)
// ------------------------------------------------
console.log("----------------------------------------")
const arr09 = [
() => new Promise(
(resolve) => {
setTimeout(() => {
console.log(10);
resolve(10);
}, 1000)
}
).then(res => { console.log(res) }),
() => new Promise(
(resolve) => {
setTimeout(() => {
console.log(20);
resolve(20);
}, 1000)
}
).then(res => { console.log(res) }),
() => new Promise(
(resolve) => {
setTimeout(() => {
console.log(30);
resolve(30);
}, 1000)
}
).then(res => { console.log(res) }),
() => (40)
]
const result09 = arr09.reduce(
(prev, next) => prev.then(next),
Promise.resolve()
)
console.log(result09)
// ------------------------------------------------
console.log("----------------------------------------")
const addOne = x => x + 1;
const addTwo = x => x + 2;
const pipe = (input) => (...functions) => functions.reduce(
(accumulator, currentFunc) => {
input = currentFunc(accumulator);
return input;
},
input
)
let initVal = 10;
let finalVal = pipe(val)(addOne, addTwo)
console.log(initVal);
console.log(finalVal)
const arr10 = [
let
]
const result10 = arr09.reduce(
(prev, next) => prev.then(next.apply()),
Promise.resolve()
)
console.log(result09)
Array.prototype.myMap = function(func, callbackThis){
// 最终返回新的数组
let res = [];
// 定义回调函数的执行环境
// call 第2个参数传入null 则this指向全部对象
let cbThis = callbackThis || null;
this.reduce(
(prev, next, index, arr)=>{
// 传入map回调函数拥有的参数
// 把每一项的执行结果push进入res
res.push(func.call(cbThis, next, index, arr));
},
null);
return res;
}
let arr11 = [1,2,3,4,5,6,7,8]
let result11 = arr11.myMap(Math.sqrt)
console.log(result11)