每日一面——深入理解reduce方法

栏目: JavaScript · 发布时间: 5年前

内容简介:有一段时间没更新了,最近挺忙的(懒病犯了)。今天偶然想到之前去去哪儿面试的时候,面试管问我的redece题目,当时被血虐的场景。干脆今天我们就来聊一下redece方法以及相关的应用reduce(callback,initval)其中callback函数接收4个参数:

引言

有一段时间没更新了,最近挺忙的(懒病犯了)。今天偶然想到之前去去哪儿面试的时候,面试管问我的redece题目,当时被血虐的场景。干脆今天我们就来聊一下redece方法以及相关的应用

reduce方法j介绍

reduce(callback,initval)

其中callback函数接收4个参数:

  • Accumulator (acc) (累计器)
  • Current Value (cur) (当前值)
  • Current Index (idx) (当前索引)
  • Source Array (src) (源数组)

如果initval传了,则索引从0开始,acc是initval,cur是arr[0]

如果initval没有传,则索引从1开始,acc是arr[0],cur是arr[1]

reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

例1:数组累加

const arr = [1,2,3,4,5];
console.log(arr.reduce((pre,cur)=>{return pre+cur}))
//1+2+3+4+5 15

例2: 计算总价

var product = [
    {
        name: '苹果',
        count: 2,
        price: 5
    },
    {
        name: '桃子',
        count: 5,
        price: 2
    },
    {
        name: '西瓜',
        count: 1,
        price: 10
    }
];
var total = product.reduce((pre,cur)=>{
    return pre+cur.count*cur.price
},0)
// 30

自己实现一个reduce方法

知道了reduce的两种模式,利用递归实现它并不复杂

// callback参数有4个。pre,cur,index,arr
Array.prototype.myReduce = function(callback,prev){
    for(let i = 0 ; i < this.length; i++){
    // 判断有没有第二个参数 
        if(!prev){ // 没有的话复杂点,第一次拿的是两个元素arr[0],arr[1],注意index的变化 
            prev = callback(this[i],this[i+1],i+1,this); //这里的指针是i+1都是对的,但是下一次循环的时候i必须按是3所以需要+1
            i++; // 第一次循环了两个变量,下次应该从第三个执行,所以向后移动
        }else{ //有的话最简单,直接就是从arr[0]开始递归
            prev = callback(prev,this[i],i,this);
        }
    }
    return prev;
}

应用

好不容易学了reduce,只计算个水果价格,是不是有点太小才大用了?

我们看一看面试中能直接大幅度提升逼格的题目

1.统计字符串中出现的字母个数

相信大家都应该知道怎么用for-in怎么遍历数组(字符串split出来的)并借助一个空对象来计数

但是面试写十几行代码还是比较low而且容易出错的

我们看一个逼格高一点的实现

var arrString = 'abcdaabc';

arrString.split('').reduce((res, cur)=>{
    res[cur] ? res[cur] ++ : res[cur] = 1
    return res;
}, {})

2.数组扁平化

面试官让实现一个原生的flat方法

// arr.flat(depth)  原生的语法 depth指定要提取嵌套数组的结构深度,默认是1 传0不扁平话,传Infinity则展开任意深度的嵌套数组

我们先不考虑depth 怎么用递归实现一版展开任意深度的flat方法
Array.prototype.myFlat = function(){
    return this.reduce((pre,cur)=>{
        if(Array.isArray(cur)){ //是个数组再递归展开一层并连接
            pre = [...pre,...cur.myFlat()] //一定要有一个去扁平化的操作
        }else{  //不是数组就直接加入结果数组中
            pre.push(cur)
        }
        return pre //注意返回的是一个数组
    },[])
}

var arr = [1,2,[3,[3]],4].myFlat()
console.log(arr)


再加上depth进行递归
Array.prototype.myflat = function(depth=1) { //实际上边界条件还是有一点问题
    return this.reduce((pre, cur) => {
        if (Array.isArray(cur)) {
            if (depth > 0) {
                depth--
                pre = [...pre,...cur.myflat(depth)]
            } else { // depth = 0结束递归
                pre.push(cur)
            }
        } else {
            pre.push(cur)
        }
        return pre
    }, [])
}

console.log([1, 2, [3,[5,[1]]], 4].myflat())   //[ 1, 2, 3, [ 5, [ 1 ] ], 4 ]
console.log([1, 2, [3,[5,[1]]], 4].myflat(0)) //[ 1, 2, [ 3, [ 5, [ 1 ] ] ], 4 ]
console.log([1, 2, [3,[5,[1]]], 4].myflat(2)) //[ 1, 2, 3, 5, [ 1 ], 4 ]
console.log([1, 2, [3,[5,[1]]], 4].myflat(Infinity)) //[ 1, 2, 3, 5, 1, 4 ]

3.实现compose函数

最后简单介绍一下compose方法以及怎么利用reduce 一行代码实现。 这可是中间件的原理,大家仔细听!

var compose = function(f,g) { //compose极简写法,仅做示例
    return function(x){
        return f(g(x))
    }

}

f和g都是函数,x是在他们之间通过'管道' 传输的值。

有个这个函数我们能干嘛呢

我们看一个例子:

我们现在想统计两个字符串的总长度 并打印:总长度:xxx

一般的写法就是写一坨

函数式编程告诉我们不要这样做,这么写耦合性太高了,不好维护,我们应该想搭积木一样,拆成若果基础方法,然后在拼接起来。 数据从这些方法组成的管道中流淌一遍出来就得到想要的结果了。

好处就是低耦合,可组合(像不像dota里面的卡尔,qwe三个球搭配可以调出N多技能)

于是我们这么写

function sum(a,b){
    return a+b;
}
function len(str){
    return str.length
}
function addPrefix(content){
    return '总长度:'+content;
}
console.log(addPrefix(len(sum('x-1','y-2'))))  //看着怪怪的

这么写最后一句话真的很烦人

于是借用compose函数我们这么写

const newFn = compose(addPrefix,len,sum)
console.log(newFn('x-1','y-2'))

那个这个compose函数的真实写法是啥呢

我给出3种,下次有时间再细说

//利用reduceRight
function compose(...fns){
    return function(...args){
        let lastFn = fns.pop();
        return fns.reduceRight((prev,current)=>{   //[addPrefix,len,sum]
            return current(prev) // 每次当前函数处理得是之前函数处理得结果!!! 但是首个不一样,首个就是首个函数得执行结果
        },lastFn(...args)) //先得把参数传入进来
    }
}


//利用reduce 
// 递归规律如下
// a:addPrefix b:len
// a:function(...args){return addPrefix(len(...args))}  b:sum
function compose(...fns){
    return fns.reduce(function(a,b){
        return function(...args){
            return a(b(...args))
        }
    })
}

//reduce简化版 一行代码搞定   面试装B必背
let compose = (...fns)=>fns.reduce((a,b)=>(...args)=>a(b(...args)));

总结

今天的汇报就到这了,谢谢大家百忙中抽出时间阅读,相信掌握上面reduce个个层次用例,面试这方面一定是满满的逼格。另外本人技术有限,如果哪写的不对,欢迎在评论区留言指出。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

51单片机应用从零开始

51单片机应用从零开始

杨欣、王玉凤、刘湘黔 / 清华大学 / 2008-1 / 39.80元

《51单片机应用与实践丛书•51单片机应用从零开始》在分析初学者认知规律的基础上,结合国内重点大学一线教师的教学经验以及借鉴国外经典教材的写作手法,对51单片机的应用基础知识进行系统而翔实的介绍。读者学习每一章之后,"实例点拨"环节除了可以巩固所学的内容外,还开辟了单片机应用的视野;再加上"器件介绍"环节,又充实了对单片机从基础到应用所需要的知识。8051单片机不仅是国内用得最多的单片机之一,同时......一起来看看 《51单片机应用从零开始》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换