一文带你彻底搞懂js的Array.prototype.reduce()方法!

一文带你彻底搞懂js的Array.prototype.reduce()方法!

定义和用法

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 = [
    [10203040],
    [10203040],
    [10203040],
    [10203040]
]

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 = [
    11111111122222222,
     22225698741263957
]

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."));

其他写法

[12345].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)




参考资料

reduce使用场景

MDN手册

Promise结合reduce构建顺序执行队列

菜鸟教程




完整示例代码

const arr01 = [102030]
const result01 = arr01.reduce((total, currentValue) => {
    return total + currentValue
}, 0)

console.log(result01)

// ---------------------------------------------------

const arr02 = [
    { x10 }, { x20 }, { x30 }
]

const result02 = arr02.reduce((total, currentValue) => {
    return total + currentValue.x
}, 0)

console.log(result02)

// ---------------------------------------------------
const arr03 = [
    [10203040],
    [10203040],
    [10203040],
    [10203040]
]

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 = [
    11111111122222222,
    22225698741263957
]

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)


原文始发于微信公众号(豆子前端):一文带你彻底搞懂js的Array.prototype.reduce()方法!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/56784.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!