资讯专栏INFORMATION COLUMN

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

Paul_King / 2790人阅读

摘要:引言有一段时间没更新了,最近挺忙的懒病犯了。例数组累加例计算总价苹果桃子西瓜自己实现一个方法知道了的两种模式利用递归实现它并不复杂参数有个。数据从这些方法组成的管道中流淌一遍出来就得到想要的结果了。

引言

有一段时间没更新了,最近挺忙的(懒病犯了)。今天偶然想到之前去去哪儿面试的时候,面试管问我的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){ // 没有prev复杂点,第一次拿的是两个元素arr[0],arr[1],注意index的变化 
            prev = callback(this[i],this[i+1],i+1,this); //这里的指针是i+1都是对的,但是下一次循环的时候i必须按是3所以需要+1
            i++; // 第一次循环了两个变量,下次应该从第三个执行,所以向后移动
        }else{ //有prev简单,直接就是从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方法
这个方法项目中经常用,但是兼容性不好,而且babel目前好像还没有这个polyfill,那我们就直接在入口文件中判断一下,如果不存在则添加数组的flat方法

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

我们先不考虑depth 怎么用递归实现一版展开任意深度的flat方法
Array.prototype.myflat = function(depth = 1) {
  return this.reduce((pre, cur) => {
    if (Array.isArray(cur)) {
      // 这里应该每次reduce都产生一个新的depth
      let _depth = depth
      if (_depth > 0) {
        _depth--
        pre = [...pre, ...cur.myflat(_depth)]
      } else { // depth = 0控制结束递归
        if (cur.length !== 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 ]
console.log([1, 2], [3]].myflat()) //[ 1, 2, 3 ]
3.实现compose函数

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

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

}
f和g都是函数,x是在他们之间通过"管道" 传输的值。
compose看起来就像是饲养函数,你就是饲养员。你可以选择多个有特点的函数,让它们结合,产生一个崭新的函数。

有个这个函数我们能干嘛呢
我们看一个例子:
我们现在想统计两个字符串的总长度 并打印:总长度: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函数利用reduce我给出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){ //compose返回的是一个函数 
            return a(b(...args)) 
        }
    })
}

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

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

列表项目

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105339.html

相关文章

  • 每日一面——仿写reverse方法

    摘要:引言今天小问了我一个面试题,怎么实现一个实方法,在实现的过程中我还是犯了一些错,实现完以后,对一些知识点的理解又加深了。此时还是在上,并且至少在当前的方法中不会再被改变了,因为没有哪个指针能指向它了。 引言 今天小K问了我一个面试题,怎么实现一个实reverse方法,在实现的过程中我还是犯了一些错,实现完以后,对一些知识点的理解又加深了。 错误的写法 最开始我是这么写的 var arr...

    未东兴 评论0 收藏0
  • JS每日一题:函数式编程中代码组合(compose)如何理解?

    摘要:期函数式编程中代码组合如何理解定义顾名思义,在函数式编程中,就是将几个有特点的函数拼凑在一起,让它们结合,产生一个崭新的函数代码理解一个将小写转大写的函数一个在字符后加的函数将两个函数组合起来这里假设我们实现了每日一题每日一题显示结果里上面 20190315期 函数式编程中代码组合(compose)如何理解? 定义: 顾名思义,在函数式编程中,Compose就是将几个有特点的函数拼凑在...

    Kaede 评论0 收藏0
  • TiDB 助力一面数据实现消费领域的决策分析平台

    摘要:深圳市一面网络技术有限公司下称一面数据是一家为消费领域的领导企业提供实时精准全面的数据洞察和决策指导的创新型企业,利用人工智能和算法,进行自然语言处理,语义情感分析,回归预测模型等,帮助客户实现精准产品运营和预测市场变化。 深圳市一面网络技术有限公司(下称:一面数据)是一家为消费领域的领导企业提供实时、精准、全面的数据洞察和决策指导的创新型企业,利用人工智能和算法,进行自然语言处理,语...

    int64 评论0 收藏0
  • 2019春招前端实习面经总结

    摘要:春招前端实习面试记录从就开始渐渐的进行复习,月末开始面试,到现在四月中旬基本宣告结束。上海爱乐奇一面盒模型除之外的面向对象语言继承因为是视频面试,只记得这么多,只感觉考察的面很广,前端后端移动端都问了,某方面也有深度。 春招前端实习面试记录(2019.3 ~ 2019.5) 从2019.1就开始渐渐的进行复习,2月末开始面试,到现在四月中旬基本宣告结束。在3月和4月经历了无数次失败,沮...

    atinosun 评论0 收藏0
  • 每日 30 秒 ⏱ 该不该优雅

    showImg(https://segmentfault.com/img/remote/1460000018734296?w=900&h=500); 简介 可读性、性能、Spread、Reduce 在 优雅三连击 中有同学提到了 可读性 这个关键词,就小二个人的观点 在某个范围内使用比较常用到的小技巧,可以提升一定的可读性,文中提到的短路运算在初始化变量是提升可读性的,并且在很多提倡优化if 语句...

    JohnLui 评论0 收藏0

发表评论

0条评论

Paul_King

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<