数组扁平化的实现

287 阅读2分钟

数组的扁平化

1. 什么是数组扁平化

所谓数组的扁平化, 就是指将一个有着多层嵌套数组转换成一个没有嵌套的数组. 例如: 将 [1, [2, 3], [4, [5, [6]]]] 转换成为 [1, 2, 3, 4, 5, 6]

const arr = [1, [2, 3], [4, [5, [6]]]]
arr.flat(3) //=> [1, 2, 3, 4, 5, 6]

2. 数组扁平化的实现

1. 最初实现

将传入的数组扁平化并返回

(function() {
  function flatten(res = []) {
    const array = this
    
    for (let i = 0, len = array.length; i < len; i ++) {
      const val = array[i]
      
      if (Array.isArray(val)) { // 如果是数组的话, 就对其进行扁平化处理
        val.flatten(res)
      } else { // 如果不是数组, 则直接将元素放入 res 即可
        res.push(val)
      }
    }
    
    return res
  }
  
  Array.prototype.flatten = flatten
})()

const array = [1, 2, [3, [4, [5, 6, [7, 8]]]]]
array.flatten() //=> [1, 2, 3, 4, 5, 6, 7, 8]

2. 进一步实现

很容易发现, 和 JS 中的 flat 相比, 最初实现少了 depth 参数 (反嵌套的深度), 在此加上

(function() {
  function flatten(depth = 1, res = []) {
    const array = this
    
    for (let i = 0, len = array.length; i < len; i ++) {
      const val = array[i]
      
      if (Array.isArray(val) && depth > 0) {
        // 如果 val 是数组, 且需要进行扁平化处理
        val.flatten(depth - 1, res)
      } else {
        res.push(val)
      }
    }
    
    return res
  }
  
  Array.prototype.flatten = flatten
})()

const array = [1, 2, [3, [4, [5, 6, [7, 8]]]]]
array.flatten(3) //=> [1, 2, 3, 4, 5, 6, [7, 8]]

虽然去掉 res 参数后更接近 flat 函数, 但是为了防止每次执行 flatten 函数都要创建一个 res 数组, 每次接受递归的返回值时还需要对其进行一次展开, 既浪费时间, 又浪费空间, 故没有去掉 res 参数. 如下所示:

(function() {
  function flatten(depth = 1) {
    const array = this
    const res = []
    
    for (let i = 0, len = array.length; i < len; i ++) {
      const val = array[i]
      
      if (Array.isArray(val) && depth > 0) {
        res.push(...val.flatten(depth - 1))
      } else {
        res.push(val)
      }
    }
    
    return res
  }
  
  Array.prototype.flatten = flatten
})()

3. 最终实现

flatten 调用到 depth === 1 时, 直接利用扩展运算符添加到 res 数组即可, 提高程序效率

不然的话, 不仅需要再递归一层, 而且每遍历一个元素还得经历多次判断

(function() {
  function flatten(depth = 1, res = []) {
    const array = this
    
    for (let i = 0, len = array.length; i < len; i ++) {
      const val = array[i]
      
      if (Array.isArray(val) && depth > 0) {
        if (depth === 1) {
          res.push(...val)
        } else {
          val.flatten(depth - 1, res)
        }
      } else {
        res.push(val)
      }
    }
    
    return res
  }
  
  Array.prototype.flatten = flatten
})()

const array = [1, 2, [3, [4, [5, 6, [7, 8]]]]]
array.flatten(3) //=> [1, 2, 3, 4, 5, 6, [7, 8]]

3. 数组扁平化的其他实现方向

1. 利用 Array.prototype.reduce 实现

(function() {
  function flatten() {
    const array = this
    const res = array.reduce((prevVal, curVal) => {
      return prevVal.concat(Array.isArray(curVal) ? curVal.flatten() : curVal)
    }, [])
    return res
  }
  
  Array.prototype.flatten = flatten
})()

2. 利用 Array.prototype.some 实现

(function() {
  function flatten() {
    const array = this
    const res = [...array]
    while (res.some(item => Array.isArray(item))) {
      res = [].concat(...res)
    }
    return res
  }
  
  Array.prototype.flatten = flatten
})()

3. 利用 Array.prototype.splice 实现

(function() {
  function flatten() {
    const array = this
    const res = [...array]
    
    for (let i = 0; i < res.length; i ++) {
      if (Array.isArray(res[i])) {
        res.splice(i, 1, ...res[i])
        i --
      }
    }
    
    return res
  }
  Array.prototype.flatten = flatten
})()

4. 利用 stack 实现

(function() {
  function flatten() {
    const array = this
    const stk = [...array]
    const res = []
    
    while (stk.length) {
      let topVal = stk.pop()
      if (Array.isArray(topVal)) {
        stk.push(...topVal)
      } else {
        res.push(topVal)
      }
    }
    
    return res.reverse()
  }
  Array.prototype.flatten = flatten
})()