每天搞透一道JS手写题💪「Day7数组扁平化的那些事(7种方法)」

251 阅读3分钟

最近在忙秋招笔试面试,结果发现实习喜欢问手写题,校招更喜欢考算法题,也是醉了。但谁让咱是苦逼的打工人呢?那就只有两手都要抓,两手都要硬了。今天总结个比较简单的内容,数组扁平化。

为了方便动手在命令行中测试我们实现的函数,这里先给出一个示例数组:

let arr = [1, [2, [3, 4]]];

console.log(flatten(arr)); // flatten 函数的返回应该是 [1, 2, 3, 4]

ES6 的 flat 方法

flatArray.prototype 上的方法,它接收一个参数 depth,表示想要提取的嵌套数组的深度,默认值为 1。

function flatten (arr) {
  return arr.flat(Infinity);
}

递归实现

使用 for 或者 forEach 遍历数组,如果遇到数组就递归的调用 flat 函数,遇到非数组元素则加入结果数组中:

function flatten(arr) {
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    Array.isArray(arr[i]) ? res = res.concat(flatten(arr[i])) : res.push(arr[i]);
  }
  return res;
}

扩展运算符实现

concat 方法创建一个新数组。该数组将首先由调用它的对象中的元素填充。然后,对于每个参数,它的值将被连接到数组中——对于普通对象或基元,参数本身将成为最终数组的一个元素;对于属性Symbol.isConcatSpreadable设置为真的数组或类数组对象,参数的每个元素都将是独立地添加到最终数组中。concat 方法不会递归到嵌套数组参数中。

利用 concat 的特性,可以展开一层嵌套数组,配合遍历检查是否仍然存在嵌套数组使用:

function flatten(arr) {
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}

这种方法的确需要反复遍历数组,因此在某些情况下可能会导致较高的时间复杂度。特别是在具有大量嵌套层数的数组中,它可能会变得非常低效。

Array.prototype.toString()

Array 对象覆盖了 Object 的 toString 方法。数组的 toString 方法实际上在内部调用了 join() 方法来拼接数组并返回一个包含所有数组元素的字符串,元素之间用逗号分隔。如果 join 方法不可用或者不是函数,则会使用 Object.prototype.toString 来代替,并返回 [object Array]。

function flatten(arr) {
  return arr.toString().split(',').map((item) => {
    return +item; 
  });
}

这种方法有一个明显的缺点:toString 方法输出的是字符串,如果不确定原先数组中元素的类型,不能保证扁平化后元素类型一致。

巧用reduce方法

这种方法和递归实现有些相似:在每次迭代中,我们检查 next 是否是一个数组。如果是数组,我们递归调用 flatten() 函数来展开它,并将结果与 prev 连接起来。如果不是数组,我们直接将 next 添加到 prev 中。

function flatten(arr) {
  return arr.reduce(function(prev, next){
    return prev.concat(Array.isArray(next) ? flatten(next) : next);
  }, []);
}

使用正则替换

简单粗暴的去除掉数组内的中括号,缺点和 toString 方法一致,转成字符串后元素类型不能保证一致。

function flatten(arr) {
  return JSON.stringify(arr).replace(/\[|\]/g, '').split(',').map(function(item){
    return +item;
  });
}

使用Generator函数

网上看到的方法。

function* flatten(arr) {
  for (let a of arr) {
    if (Array.isArray(a)) {
      yield* flatten(a);
    } else {
      yield a;
    }
  }
}
let arr = [1,[2,[3,[4,[5]]]]];
for (let f of flatten(arr)){
  console.log(f);
}