实现累加求和_浅析reduce方法实现及场景

本文探讨了JavaScript中Array.prototype.reduce方法的用途,详细解释了reduce的工作原理,并提供了实现reduce的基本示例。文章通过数组求和、去重、扁平化、Promise链式操作、compose函数等应用场景来展示reduce的灵活性。同时,还介绍了如何使用reduce实现一个简单的计数器功能。
摘要由CSDN通过智能技术生成

缘起

js中Array.prototype.reduce方法在业务编码中很少用到,但是面试题中经常被用到,比如实现一个reduce方法、数组去重、实现compose函数、数组扁平化等等。为了能够更好的了解reduce功能和使用场景,准备重新学习reduce的定义和实现。

定义

reduce 方法对数组中的每个元素执行一个reducer函数(回调函数), 最终汇总结果,返回一个值。

reduce使用方法(语法):

Array.prototype.reduce(reducer(acc, cur[, idx[, src]])[, initValue])

接收两个参数:reducer方法,initValue

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

  1. Accumulator (acc) (累计器)

  2. Current Value (cur) (当前值)

  3. Current Index (idx) (当前索引)

  4. Source Array (src) (源数组)

reducer方法的返回值返回给acc(累加器),作为下一次迭代的入参,直到迭代结束作为结果返回。

另外一个重要的参数:initValue, 作为累加器初始值,如果使用时没有initValue,则累加器初始值就是数组中的第一个元素;换个角度,initValue影响迭代的次数,如果initValue有值,则迭代次数为数组的长度,如果没有值,迭代次数比数组长度少1。可以看一个简单的例子:

var array = [1, 3, 5, 7, 9]var sum = array.reduce((acc, value, i) =>{    console.log('第' + i + '次迭代', 'acc值:' + acc, '当前值:' +  value)    return  (acc + value)})console.log(sum)var sum1 = array.reduce((acc, value, i) => {    console.log('第' + i + '次迭代', 'acc值:' + acc, '当前值:' +  value)    return (acc + value)}, 10)console.log(sum1)

8a49d459890663cecf1827329b62af2d.png

理解这一点,对实现reduce很关键。

实现

reduce实现的版本很多,我们这边按照最简方式来实现,具体代码如下:

function reduce(array, callback, initValue) {    const { length } = array;    if (!length) return   let startIndex = 1  let acc = array[0]  if (initValue) {    acc = initValue    startIndex = 0  }    for(let i = startIndex; i < length; i++) {        const value = array[i]    acc = callback(acc, value, i, array)    }  return acc}

MDN参考文档里面有pollify版本

// Production steps of ECMA-262, Edition 5, 15.4.4.21// Reference: http://es5.github.io/#x15.4.4.21// https://tc39.github.io/ecma262/#sec-array.prototype.reduceif (!Array.prototype.reduce) {  Object.defineProperty(Array.prototype, 'reduce', {    value: function(callback /*, initialValue*/) {      if (this === null) {        throw new TypeError( 'Array.prototype.reduce ' +           'called on null or undefined' );      }      if (typeof callback !== 'function') {        throw new TypeError( callback +          ' is not a function');      }      // 1. Let O be ? ToObject(this value).      var o = Object(this);      // 2. Let len be ? ToLength(? Get(O, "length")).      var len = o.length >>> 0;  // 如果疑惑 参考 https://stackoverflow.com/questions/1822350/what-is-the-javascript-operator-and-how-do-you-use-it      // Steps 3, 4, 5, 6, 7      var k = 0;      var value;      if (arguments.length >= 2) {        value = arguments[1];      } else {        while (k < len && !(k in o)) {          k++;        }        // 3. If len is 0 and initialValue is not present,        //    throw a TypeError exception.        if (k >= len) {          throw new TypeError( 'Reduce of empty array ' +            'with no initial value' );        }        value = o[k++];      }      // 8. Repeat, while k < len      while (k < len) {        // a. Let Pk be ! ToString(k).        // b. Let kPresent be ? HasProperty(O, Pk).        // c. If kPresent is true, then        //    i.  Let kValue be ? Get(O, Pk).        //    ii. Let accumulator be ? Call(        //          callbackfn, undefined,        //          « accumulator, kValue, k, O »).        if (k in o) {          value = callback(value, o[k], k, o);        }        // d. Increase k by 1.        k++;      }      // 9. Return accumulator.      return value;    }  });}

应用

数组求和/求积
var array = [1, 2, 3, 4]var sum  = array.reduce((acc, value) => (acc + value))
数组去重

除了利用Set去重,也可以使用reduce实现【比较常见面试题】

var array = [1, 2, 2, 4, 3, 3, 5]var result = array.reduce((acc, value) => {if(acc.indexOf(value) === -1) {    acc.push(value)}return acc;}, [])
数组扁平化

简单场景:二维转一维

var array = [1, [1, 3], 4, [5, 6, 7]]var flatten =  function(arr) {    return array.reduce((acc, value) => {    acc = acc.concat(value)    return acc}, [])}flatten(array)//  [1, 1, 3, 4, 5, 6, 7]

复杂一点:多维转一维,需要用到递归知识

var array = [1, [1, [3]], 4, [5, [6, 7]]]var flatten = function(arr) {  return arr.reduce((acc, value) => {        return  acc.concat(Array.isArray(value) ? flatten(value) : value)  }, [])}flatten(array)
promise链式操作

按照顺序执行

function p1(num) {    return new Promise((resolve, reject) => {        resolve(num + 1)    })}function p2(num) {    return new Promise((resolve, reject) => {        resolve(num * 3)    })}function p3(num) {    return new Promise((resolve, reject) => {        resolve(num * 2)    })}var array = [p1, p2, p3]var promiseChian= function(array, initValue ) {    return array.reduce((acc, item) => {            return acc.then(item)    }, Promise.resolve(initValue))}promiseChian(array, 5).then(res => {console.log(res)})// 36
compose函数(组合函数)

入门场景

function compose(f, g) {    return function(x) {        return f(g(x))    }}// demovar test = 2function add(x) {    return x + 10}function multiply(x) {    return x * 2}compose(add, multiply)(test)// 14

compose函数:调用时,组合函数按顺序从右向左执行,右边函数调用后,返回的结果,作为左边函数的参数传入。

function f1(arg) {    console.log(arg, 'f1')  return arg + 1}function f2(arg) {    console.log(arg, 'f2')  return arg + 2}function f3(arg) {    console.log(arg, 'f3')  return arg + 3}function f4(arg) {    console.log(arg, 'f4')  return arg + 4}// 这样看起来有些懵 且不易维护f4(f3(f2(f1(1)))) // 输出:11// 可以利用compose函数来实现这个功能compose(f1, f2, f3, f4)(1)

下面使用 reduce 来实现compose

function compose() {    var fns = Array.prototype.slice.call(arguments)    return (arg) => {    if(fns.length === 0) {            return () => {}        } else if(fns.length === 1){            return fns[0]        }        return fns.reverse().reduce((acc, value) => {            return value(acc) // 每次当前函数处理之前函数的结果,其中acc初始值就是入参        }, arg)    }}

还有一种面试版本(1行代码)

var compose = (...fns)=> fns.reduce((a, b) => (...arg) => a(b(...arg)))

根据简单的例子可以分析一下这个方法:compose(f4, f3, f2, f1)('hello', 'world')

  • reduce第一次执行:返回值为 函数  (...arg)=> f4(f3(...arg)), 作为下一次 执行 a 的值

  • 第二次执行:返回值为 函数 (...arg)=> f4(f3(f2(...arg))), 作为下一次执行 a的值

  • 第三次执行:返回值为 函数  (...arg) => f4(f3(f2(f1(...arg))))

compose函数返回值是一个函数,compose(f4, f3, f2, f1)('hello', 'world')实际执行的方法就是

f4(f3(f2(f1('hello', 'world'))))

计数器

统计数组中各个元素出现的次数,返回结果对象

var countItem = function(arr) {    return arr.reduce((acc, value) => {    if(!acc[value]) {         acc[value] = 1        } else {         acc[value]++        }    return acc  }, {})}var array = ['a', 'b', 'c', 'h', 'o', 'a', 'b', 'c', 'a']countItem(array)// {a: 3, b: 2, c: 2, h: 1, o: 1}

参考

  1. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

  2. https://github.com/reduxjs/redux/blob/v3.7.2/src/compose.js (redux中compose实现方法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值