JS手写数组扁平化(flat)方法

1,489 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情

方法介绍

数组扁平化方法 Array.prototype.flat() 也叫数组拍平、数组拉平、数组降维。

Array.prototype.flat() 用于将嵌套的数组“拉平”,变成一维的数组。

该方法返回一个新数组,对原数据没有影响。

  • 不传参数时,默认“拉平”一层,可以传入一个整数,表示想要“拉平”的层数。
  • 传入 <=0 的整数将返回原数组,不“拉平”。
  • Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组。
  • 如果原数组有空位,Array.prototype.flat() 会跳过空位。

代码示例:

const arr = [1,[2,3],[4,[5,[6]],7]];

// 不传参数时,默认“拉平”一层
arr.flat() 
// [1,2,3,4,[5,[6]],7];

// 传入一个整数参数,整数即“拉平”的层数
arr.flat(2) 
// [1,2,3,4,5,[6],7];

// Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组
arr.flat(Infinity);
// [1,2,3,4,5,6,7];

// 传入 <=0 的整数将返回原数组,不“拉平”
arr.flat(0);
// [1,[2,3],[4,[5,[6]],7]]
arr.flat(-6);
// [1,[2,3],[4,[5,[6]],7]]

// 如果原数组有空位,flat()方法会跳过空位
[1,2,3,4,5,6,,].flat();
// [1,2,3,4,5,6]

 开始手写

思路: 实现一个有数组扁平化功能的 flat 函数,要做的就是在数组中找到是数组类型的元素,然后将他们展开。这就是实现数组拍平 flat 方法的关键思路。

遍历数组方案:

for 循环;for...of;for...in;forEach();entries();keys();values();reduce();map()

判断元素是数组方案:

instanceof;constructor;Object.prototype.toString;isArray

将数组的元素展开一层方案:

扩展运算符(…) ; concat;

apply:主要是利用 apply 在绑定作用域时,传入的第二个参数是一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。也就是在调用 apply 函数的过程中,会将传入的数组一个一个的传入到要执行的函数中,也就是相当对数组进行了一层的展开。

使用forEach+push递归实现:

const arr = [1,[2,3],[4,[5,[6]],7]];

function func(array) {
    let newArr = []
    const rec = (arr) => {
      arr.forEach(item => {
        if (!Array.isArray(item)) {
          newArr.push(item)
        } else {
          rec(item)
        }
      })
    }
    rec(array)
    return newArr
  }
  let res = func(arr)
  console.log(res) //[1,2,3,4,5,6,7]

 使用for循环+concat实现:

const arr = [1,[2,3],[4,[5,[6]],7]];

function flatten(arr) {
    let result = [];
    for (let i = 0; i < arr.length; i++) {
      if (Array.isArray(arr[i])) {
        result = result.concat(flatten(arr[i]));
      } else {
      result = result.concat(arr[i])
      }
    }
    return result
  }
  
  console.log(flatten(arr));//[1,2,3,4,5,6,7]

增加参数控制扁平化深度,可以理解为手写flat()方法:

const arr = [1,[2,3],[4,[5,[6]],7]];

//版本1:
// forEach 遍历数组会自动跳过空元素
const eachFlat = (arr = [], depth = 1) => {
    const result = []; 
    (function flat(arr, depth) {
      arr.forEach((item) => {
        if (Array.isArray(item) && depth > 0) {
          flat(item, depth - 1)
        } else {
          result.push(item)
        }
      })
    })(arr, depth)
    return result;
  }
  console.log(eachFlat(arr,2))//[1,2,3,4,5,[6],7]
  
  //版本2:
  // 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;
  }
  console.log(forFlat(arr,2))//[1,2,3,4,5,[6],7]

巧用reduce方法实现:

有关reduce方法的使用方法和具体细节请看我的这篇文章:戳我传送

const arr = [1,[2,3],[4,[5,[6]],7]];

const flatten = (arr, deep = 1) => {
    if (deep <= 0) return arr;
    return arr.reduce((res, curr) => res.concat(Array.isArray(curr) ? flatten(curr, deep - 1) : curr), [])
}

console.log(flatten(arr, Infinity));//[1,2,3,4,5,6,7]