lodash源码阅读-----由zip和unzip实现的数组的分组合并

544 阅读2分钟

用法

zip方法接收传入多个数组,它会创建分组元素的数组,第一个数组包含给定数组的第一个元素,第二个元素包含给定数组的第二个元素,依此类推,最后返回这个数组。

unzip方法和zip方法的用法近似相反,只是它接受一个分组数组元素并创建一个数组,将元素重新组合到它们的预压缩配置。

来看看用法

 const zipped = zip(['a', 'b'], [1, 2], [true, false])
 // => [['a', 1, true], ['b', 2, false]]
 
 unzip(zipped)
 // => [['a', 'b'], [1, 2], [true, false]]

解析

我们先看zip方法的源码

function zip(...arrays) {
  return unzip(arrays)
}

由于zip方法是通过调用unzip方法实现的,所以我们直接来看unzip方法的实现。

function unzip(array) {
  if (!(array != null && array.length)) {
    return []
  }
  let length = 0
  array = filter(array, (group) => {
    if (isArrayLikeObject(group)) {
      length = Math.max(group.length, length)
      return true
    }
  })
  let index = -1
  const result = new Array(length)
  while (++index < length) {
    result[index] = map(array, baseProperty(index))
  }
  return result
}

函数首先对数组进行了检测,以此确保数组有意义,之后进一步做了循环,来看看这个循环。

 let length = 0
  array = filter(array, (group) => {
    if (isArrayLikeObject(group)) {
      length = Math.max(group.length, length)
      return true
    }
  })

该循环使用isArrayLikeObject方法对数组中的每个目标数组进行了检测,确保其有意义,并且将length赋值为子数组的最大长度,以此确定合并后的数组长度。

之后看看isArrayLikeObject方法,这个方法做了两步检测

  
function isArrayLikeObject(value) {
  return isObjectLike(value) && isArrayLike(value)
}

function isArrayLike(value) {
  return value != null && typeof value !== 'function' && isLength(value.length)
}

function isObjectLike(value) {
  return typeof value === 'object' && value !== null
}

  • isArrayLike方法,除了检测value不为空和function外,还检测它是否具有length属性,目的是筛选出不为数组,但是具有length属性的元素,如string,document.body.children

  • isObjectLike方法进一步检测了value必须是一个不为null的对象

所以这里排除了string,value只能为数组,document.body.children,或arguments等类数组。

看完接着看函数后面的代码

let index = -1
  const result = new Array(length)
  while (++index < length) {
    result[index] = map(array, baseProperty(index))
  }
  return result

这里有两个遍历,while循环的循环长度是,子数组的最大长度,然后在循环内部,再将子数组相同位置的元素放如合并数组。这里map的传入函数是baseProperty,来看看它的实现。

function baseProperty(key) {
  return (object) => object == null ? undefined : object[key]
}

这里返回了一个函数,置入map的话就是这样的,目的是为了去掉长度不对等的子数组中的空元素。

result[index] = map(array, item => {
    item == null ? underfined : object[index]
})

总结

zip和unzip方法可以实现数组的分组和合并,源码实现并不难,还是主要通过两层的遍历实现的,但是考虑了很多的边界条件。

想到了一个使用场景:假如现在有一个数组存了每个学生的年龄,一个数组存了每个学生的姓名,现在需要拆分成单个的学生对象,那就可以用unzip来进行分组,之后再转化为对象,就不用多次的遍历。


如果你觉得对你有用的话,不妨留个赞呀~~~^-^