lodash里的sum和mean

573 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

使用说明

sum

lodash里的sum方法主要是计算一组数组数据的总和,该方法接受一个数组参数。

_.sum([])
// => 0

_.sum([1,2,3])
// => 6

_.sum([1,'2',3])
// => '123'

_.sum([1,'2',{}])
// =>'12[object Object]'

_.sum(344)
// => 0

_.sum({})
// => 0

_.sum()
// => 0

mean

lodash里的mean方法主要是计算一组数组数据的平均值,该方法接受一个数组参数。

_.mean([1,2,3,4,5])
// => 3

_.mean([1,'2'])
// => 6

_.mean([1,{}])
// => NaN

_.mean()
// => NaN

从结果可见mean方法是对每一项进行相加再除以个数,除法操作触发隐式转换使得返回值是数字类型。

手写实现

求指定数组的总和

function sum(array) {
    if (!Array.isArray(array)) return NaN
    let result = 0
    array.forEach(item => result += item)
    return result
}

在上面的求和过程中,我们借助一个中间变量来存储记录计算的值。通过一次循环遍历每一个值,在时间复杂度上为O(n)。

求指定数组平均值

function mean(array) {
    if (!Array.isArray(array)) return NaN
    let result = 0
    array.forEach(item => result += item)
    return result / array.length
}

根据求和的思路,我们通过借助一个中间变量来存储记录计算的值,在返回结果上除以数组的长度。通过一次循环遍历每一个值,在时间复杂度上为O(n)。

源码实现

sum

我们查看lodash源码的sum方法,发现它最先判断传入的参数是否存在,并且仅当参数存在且参数身上存在length属性时,才调用baseSum方法,否则返回结果0。

function identity(value) {
  return value;
}

function sum(array) {
  return (array && array.length)
    ? baseSum(array, identity)
    : 0;
}

在这里baseSum传入两个参数,第一个参数为源数据,第二个参数为验证方法。 通过前几个篇章的了解,我们猜测baseSum可能在多个地方用到,最起码求平均值是在求和基础上进行的。

查看baseSum源码,不同于我们用forEach的实现,其内部遍历通过while进行,依旧通过中间变量result记录计算结果。

在baseSum方法中,其主要剔除了值为undefined的项。由于初始化时result是undefined,所以在if逻辑处理里并不是直接加上数组项,如果是第一次循环,则取数组的第一项赋值到result。

function baseSum(array, iteratee) {
  var result,
      index = -1,
      length = array.length;

  while (++index < length) {
    var current = iteratee(array[index]);
    if (current !== undefined) {
      result = result === undefined ? current : (result + current);
    }
  }
  return result;
}

mean

查看lodash里的mean源码实现,我们发现其不同于sum方法一开始的判断,对于任何参数是直接传递给baseMean方法的。

function identity(value) {
  return value;
}

function mean(array) {
  return baseMean(array, identity);
}

查看baseMean方法,发现内部调用了求和里的baseSum方法,所以我们也理解了其封装的必然性。

而对于NaN,其通过0/0获取赋值。

var NAN = 0 / 0;

function baseMean(array, iteratee) {
  var length = array == null ? 0 : array.length;
  return length ? (baseSum(array, iteratee) / length) : NAN;
}

在该求平均值的方法中,其会验证参数是否存在length属性,没有的话返回NaN,存在的话则调用baseSum方法。

实际上mean方法依旧可以对字符串进行求平均值操作,具体结果则是字符串转数字类型再除以该字符串的length长度。

小结

通过源码实现,我们了解到mean方法主要基于sum方法。二者通过循环遍历,用中间变量存储累加的计算结果,mean方法则在sum方法的结果上除以数组的长度。