学习一下数组扁平化

79 阅读3分钟

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

学习一下数组扁平化

start

  • 经常看到有数组扁平化的题目。
  • 看别人写的上来就好几种数组扁平化的方式,自己亲手写一写,变成自己的知识。

1. flat

ES6 在数组上新增的方法:Array.prototype.flat ;

const arr1 = [0, 1, 2, [3, 4]]
console.log(arr1.flat())
// expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]]
console.log(arr2.flat(2))
// expected output: [0, 1, 2, [3, 4]]

const arr3 = [0, 1, 2, [[[3, 4]]]]
console.log(arr3.flat(Infinity))
// expected output: [ 0, 1, 2, 3, 4 ]

个人总结

  • flat:英文释义:平的,平坦的;
  • arr.flat([depth]), depth 指定要提取嵌套数组的结构深度,默认值为 1;
  • 返回一个包含将数组与子数组中所有元素的新数组;(扁平化不会修改原数组
  • 使用 Infinity,可展开任意深度的嵌套数组;
  • flat() 方法会移除数组中的空项:

2. toString + split + map

var arr = [1, 2, 3, [4, 5, [6, 7, 8]]]

var newArr = arr
  .toString()
  .split(',')
  .map((item) => parseFloat(item))

个人总结

  • 只针对纯数字的多维数组;
  • 数组项存在字符串,例如:[1,2,'3'],使用这种方法,就会修改原数组;
  • map 的作用就是用来还原数组每一项的类型;
  • 字符串转数组,方法很多: parseFloat+

3. reduce + concat + isArray + recursivity(递归)

// 使用 reduce、concat 和递归展开无限多层嵌套的数组
var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]]
function flatDeep(arr, d = 1) {
  return d > 0
    ? arr.reduce(
        (acc, val) =>
          acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val),
        []
      )
    : arr.slice()
}

flatDeep(arr1, Infinity)
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

个人总结

  • 主要思想就是递归;
  • 其次就是 reduce 的基本用法;
  • 再复习一下 reduce 的基础用法:
    • 两个参数,一个参数为回调函数,一个参数为初始值;
    • 回调函数的参数依次为:上一次调用的返回值;当前项;当前项索引;正在处理的数组;

4.forEach + isArray + push + recursivity(递归)

// forEach 遍历数组会自动跳过空元素
const eachFlat = (arr = [], depth = 1) => {
  const result = [] // 缓存递归结果
  // 开始递归
  ;(function flat(arr, depth) {
    // forEach 会自动去除数组空位
    arr.forEach((item) => {
      // 控制递归深度
      if (Array.isArray(item) && depth > 0) {
        // 递归数组
        flat(item, depth - 1)
      } else {
        // 缓存元素
        result.push(item)
      }
    })
  })(arr, depth)
  // 返回递归结果
  return result
}

// for of 循环不能去除数组空位,需要手动去除
const forFlat = (arr = [], depth = 1) => {
  const result = []
  ;(function flat(arr, depth) {
    for (let item of arr) {
      if (Array.isArray(item) && depth > 0) {
        flat(item, depth - 1)
      } else {
        // 去除空元素,添加非 undefined 元素
        item !== void 0 && result.push(item)
      }
    }
  })(arr, depth)
  return result
}

个人总结

  • 循环加递归;
  • 需要注意的是 for 循环不会跳过空元素,forEach 会,例如:([1,,3].forEach(i=>{console.log('tomato',i)}))。

5. 使用堆栈 stack

// 无递归数组扁平化,使用堆栈
// 注意:深度的控制比较低效,因为需要检查每一个值的深度
// 也可能在 shift / unshift 上进行 w/o 反转,但是末端的数组 OPs 更快
var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]]
function flatten(input) {
  const stack = [...input]
  const res = []
  while (stack.length) {
    // 使用 pop 从 stack 中取出并移除值
    const next = stack.pop()
    if (Array.isArray(next)) {
      // 使用 push 送回内层数组中的元素,不会改动原始输入
      stack.push(...next)
    } else {
      res.push(next)
    }
  }
  // 反转恢复原数组的顺序
  return res.reverse()
}
flatten(arr1) // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

个人总结

  • 基本原理就就是遇到数组就解构。

6. Use Generator function

function* flatten(array) {
  for (const item of array) {
    if (Array.isArray(item)) {
      yield* flatten(item)
    } else {
      yield item
    }
  }
}

var arr = [1, 2, [3, 4, [5, 6]]]
const flattened = [...flatten(arr)]
// [1, 2, 3, 4, 5, 6]

个人总结

  • Generator 的说明
  • flatten(arr)返回的是遍历器对象,然后通过解构运算符,依次执行。
  • 本质还是递归的思想。

end

  • 数组扁平化总结:
    • 不修改原数组;
    • 如果需要手写实现,优先考虑递归;