语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
参数
参数 | 描述 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
function(total,currentValue, index,arr) | 必需。用于执行每个数组元素的函数。 函数参数:
| ||||||||||
initialValue | 可选。传递给函数的初始值 |
eg:
let expr = ['message','a']
let data = {
message: {
a: 1
}
}
// 让data.message.a = 2
let b = expr.reduce((prev,next,currentIndex)=>{
// 遍历到最后一层.xx时赋值给message.a = value
if(currentIndex === expr.length-1){
return prev[next] = 2;
}
return prev[next];
},data)
console.log(b)
最后b=>data.message.a的值就变成2了
常见的reduce现实应用:
1. 求和
function calcSum(...args) {
const arr = [...args]
let sum = 0;
return arr.reduce((obj, cur) => {
sum = obj + cur
return sum
}, sum)
}
求和思想也可以利用在二进制转十进制方法中:
// 小数部分 => 数字*(2的index次方)
function eachBinaryDigitPartToDecimal(binaryDigitPartArr) {
return binaryDigitPartArr.map((currentValue, index) => {
return Number(currentValue) * Math.pow(2, (-(index + 1)))
})
}
// 如果该二进制只有整数部分则直接用 parseInt(string, radix) 处理
function eachBinaryIntPartToDecimal(binaryDigit) {
return parseInt(binaryDigit, 2)
}
/**
* 将二进制小数(包含整数部分和小数部分)转换为十进制数
* @param binaryDigit 二进制数(可能是整数,也可能是小数)
*/
function floatToDecimal(binaryDigit) {
// 如果该二进制只有整数部分则直接用 parseInt(string, radix) 处理
if (Number.isInteger(binaryDigit)) {
return eachBinaryIntPartToDecimal(binaryDigit)
} else {
// 将整数和小数部分的数字分开组成一个数组, 第一个元素是整数部分的数字, 第二个元素是小数部分的数字
const binaryDigitNumArr = binaryDigit.toString().split(".")
// 将二进制整数转换为十进制数
const binaryIntParStr = binaryDigitNumArr[0]
const decimalIntPartNum = eachBinaryIntPartToDecimal(binaryIntParStr)
// 将二进制小数部分转换为十进制数
const binaryDigitPartArr = binaryDigitNumArr[1].split("") // 将数字split成小数部分的每个数字作为元素的一元数组
const eachDecimalFloatPartNum = eachBinaryDigitPartToDecimal(binaryDigitPartArr) // 数组的数字变成item * 2^(-index)
const deciamlFloatPartNum = eachDecimalFloatPartNum.reduce((val, currentVal) => val + currentVal) // 数组元素相加
return decimalIntPartNum + deciamlFloatPartNum
}
}
console.log(floatToDecimal(1111011.111)) // 123.875
console.log(floatToDecimal(1111011)) // 123
console.log(floatToDecimal(0.111)) // 0.875
2. 将url的search参数转化为对象
const URL_SEARCH ='?source=baidupc_utmId_5100&plan=A005-qglrgmc&unit=a010-mp4&keyword=mp4zgif&keywordid=20210343653&bd_vid=7019318071742353099'
const urlObj = URL_SEARCH.slice(1) // 去掉第一个?号字符
.split('&')
.filter(Boolean)
.reduce((obj, cur)=> {
let arr = cur.split('=')
const idx = cur.indexOf('=') // 获取第一个=号所在的下标
// if(arr.length !== 2) {
// return obj;
// }
obj[(cur.substring(0, idx))] = encodeURIComponent(cur.substring(idx+1));
console.log(obj, cur)
return obj;
}, {})
const getQueryString = (key) => urlObj[key]
console.log(getQueryString('unit'))
3. 让Promise依次执行
function runPromises(promiseCreators, initData) {// promiseCreators能够产生promise的数组
return promiseCreators
.reduce(
(promise, next) => {
return promise.then((data) => next(data))
},
Promise.resolve(initData)
)
}
function login(data) {
console.log("login: data", data);
return new Promise(resolve => {
setTimeout(() => {
return resolve({
token: 'token'
})
}, 500)
})
}
function getUserInfo(data) {
console.log("getUserInfo: data", data);
return new Promise(resolve => {
setTimeout(() => {
return resolve({
name: 'user-1',
id: 988
})
}, 300)
})
}
function getOrders(data) {
console.log("getOrders: data", data);
return new Promise(resolve => {
setTimeout(() => {
return resolve([{
orderId: 1,
productId: 100,
price: 100
}])
}, 100)
})
}
const initData = {
name: 'nam1',
pwd: 'pwd1'
}
runPromises([login, getUserInfo, getOrders],
initData)
.then(res => {
console.log("res", res);
})
相当于promise的链式调用:
// Promise.resolve(initData)
// .then(data => login(data))
// .then(data => getUserInfo(data))
// .then(data => getOrders(data))
// .then(data => console.log("orders", data))
4. 数组分组
function group(arr, fn) {
// 不是数组
if (!Array.isArray(arr)) {
return arr
}
// 不是函数
if (typeof fn !== 'function') {
throw new TypeError('fn必须是一个函数')
}
var v
return arr.reduce((obj, cur, index) => {
v = fn(cur, index)
if (!Reflect.hasOwnProperty.call(obj, v)) {
obj[v] = []
}
obj[v].push(cur)
return obj
}, {})
}
// 按照字符串长度分组
let products = ["apple", "pear", "orange", "peach"];
const f1 = v => v.length
console.log(
group(products, f1),
);
// 按照分数分组
result = [{
name: "tom",
score: 60
}, {
name: "Jim",
score: 40
}, {
name: "Nick",
score: 88
}]
const fn = v => v.score >= 60
console.log(
group(result, fn),
);
5. 模拟compose, pipe方法:
const compose = (...funcs) => {
if (funcs.length === 0) return arg => arg
return funcs.reduce((v, cur) => (...args) => (v(cur(...args))))
}
// 注意: compose执行顺序就是从右往左依次传入的函数方法
const pipe = (...funcs) => {
if (funcs.length === 0) return arg => arg
return funcs.reduce((a, b) => (...args) => (b(a(...args))))
}
// pipe执行顺序是从左往右依次传入的传函数方法
例如几个优惠叠加计算:
// 九折优惠
function discount(x) {
console.log('discount')
return x * 0.9
}
// 满200减50
function reduce(x) {
console.log('reduce')
return x >= 200 ? x - 50 : x
}
// 优惠plus
const discountPlus = (x) => {
console.log('discountPlus')
return x * 0.95
}
const compose = (...funcs) => {
if (funcs.length === 0) return arg => arg
return funcs.reduce((v, cur) => (...args) => (v(cur(...args))))
}
// 先discount打折, 再reduce优惠, 再discountPlus打折
// compose执行顺序就是从后往前的
const getPrice = compose(discountPlus, reduce, discount)
print(getPrice(200), getPrice(250))
const pipe = (...funcs) => {
if (funcs.length === 0) return arg => arg
return funcs.reduce((a, b) => (...args) => (b(a(...args))))
}
// pipe参数从左往右依次传优惠
const getPrice2 = pipe(discount, reduce, discountPlus)
print(getPrice2(200), getPrice2(250))