JavaScript数组扁平化+面试

引言

在 JavaScript 编程中,处理多维数组是一个常见的任务。数组扁平化是将嵌套的多维数组转换为一维数组的过程。呆同学今天想和大家分享几种常见的数组扁平化方法,包括使用内置方法 flat(), 递归函数, toString(), reduce(), 以及解构和 concat()。与此同时,如果各位在面试时,遇到需要你将一个多维数组扁平化但不能用内置方法时,我们如何手写一个函数将其扁平化。话不多说,我们一起来看看吧。

Array.flat()

flat() 是 JavaScript 中的内置方法,它可以将嵌套的数组"拉平",也就是把多维数组转化为一维数组。 语法如下:

语法 复制代码
arr.flat([depth])

depth(可选):指定要提取嵌套数组的结构深度,默认值为 1。如果传入 Infinity,则会完全扁平化数组。我举个例子使用flat(Infinity)来扁平化数组:

js 复制代码
const arr = [1, [2, [3, [4, 5]]]];
const flatArr = arr.flat(Infinity);
console.log(flatArr); // 输出: [1, 2, 3, 4, 5]

在这个例子中,arr数组多层嵌套,但我们可以使用flat(Infinity)将其元素全部提取出来并放入一个一维数组中。

这个方法的优点显而易见,简单直观,极大地简化了数组的处理过程,并且在flat(Infinity)的使用下可以提高代码效率。但是,当它在处理非常大的数组或深度嵌套的数组时,带来的性能开销也是巨大的。因此需要根据具体情况权衡而定。

递归

上面讲到了 JavaScript 中的内置方法将数组扁平化,但如果你在面试时,面试官肯定是不愿意你用的,不然这样考察不到你的能力和理解。那么接下来我们可以根据flat()方法的理解,自己手写一个flatten()方法。那么,自己手写方法的过程中,递归就是一个非常好的将数组扁平化的方法。

通过flat()方法的理解我们可以知道,要想将数组扁平化,就是将多层嵌套的数组元素取出将其存放在一维数组中。递归则可以先遍历所有元素,只要遇到数组元素,就将这个子数组遍历,直到所有元素都放入一个新的一维数组中。以下便是一个使用直接递归来扁平化数组的例子:

js 复制代码
const arr = [1, 2, [3, [4, 5]]];
function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flatten(arr[i])); // concat合并数组,返回新数组
        } else {   
            res.push(arr[i]);
        }
    }
    return res;
}

const newArr = flatten(arr);
console.log(newArr);// 输出: [1, 2, 3, 4, 5

递归方法的优点在于代码逻辑直观好理解,适用于任意深度的多维数组,缺点则是如果递归层级过深时,导致栈溢出,因为每次递归都会调用栈内存,这样栈空间会耗尽。

递归 + reduce()

结合 reduce() 方法和递归可以实现数组的扁平化。reduce() 方法用于将数组的每个元素累加到一个累加器中,递归用于处理数组元素是数组的情况。

以下是结合 reduce() 和递归来实现数组扁平化的例子:

js 复制代码
const arr = [1, 2, [3, [4, 5]]];

function flatten(arr) {
    return arr.reduce((pre, item) => {
        return pre.concat(Array.isArray(item) ? flatten(item) : item);
    }, [])
}
console.log(flatten(arr)); // 输出: [1, 2, 3, 4, 5]

补充一个知识点:concat() 是 JavaScript 中数组对象的一个方法,用于连接两个或多个数组,并返回一个新的数组,而不修改现有数组。

flatten 函数内部,我们使用了 reduce() 方法来迭代数组中的每个元素。reduce() 方法接受一个回调函数和一个初始值。回调函数接受两个参数:累加器 pre 和当前元素 item。在每次迭代中,回调函数都会处理当前元素,并将结果累加到累加器中。

在回调函数中,我们首先使用条件运算符 Array.isArray(item) 来检查当前元素是否为数组。如果是数组,则说明需要进一步扁平化处理。这时候我们就会递归调用 flatten() 函数来处理这个子数组 item,直到子数组不再包含数组为止。

一旦当前元素不再是数组,而是一个简单的数值,我们就不再递归调用 flatten(),而是直接将该数值添加到累加器 pre 中。

最终,reduce() 方法会返回一个扁平化后的数组,其中所有嵌套的数组都已经被展开。这个数组就是我们想要的结果。

解构

js 复制代码
const arr = [1, 2, [3, [4, 5]]];

function flatten(arr) {
  const result = [];
  while (arr.length) {
    const [first, ...rest] = arr;
    if (Array.isArray(first)) {
      arr = [...first, ...rest]; // 如果第一个元素是数组,将其展开并与剩余元素合并
    } else {
      result.push(first); // 如果第一个元素不是数组,添加到结果数组中
      arr = rest; // 更新数组,将剩余元素赋值给 arr
    }
  }
  return result;
}

const flattenedArr = flatten(arr);
console.log(flattenedArr); // 输出: [1, 2, 3, 4, 5]

我们通过while循环遍历数组,使用解构语法,将数组分为第一个元素和剩余元素,如果第一个元素还是数组元素,则继续分离,如果不是,则加入到新的一维数组中。

解构 + some()

js 复制代码
const arr = [1, 'abc', [3, 4, [5]]];

function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr); // 使用解构将子数组展开
    }
    return arr;
}

console.log(flatten(arr)); // 输出: [1, 'abc', 3, 4, 5]

Array.prototype.some() 是 JavaScript 数组对象的方法,用于测试数组中的至少一个元素是否通过由提供的函数实现的测试。它返回一个布尔值,即如果数组中至少有一个元素满足测试条件,则返回 true,否则返回 false

我们使用 some 方法遍历数组,检查数组中是否包含任何子数组。如果找到一个子数组,就返回 true,否则返回 false

当数组中包含子数组时,则使用解构和 concat 方法将当前数组中的所有元素展开一级。concat 方法结合解构赋值 ... 用于将数组的每个元素(包括子数组)展开并连接成一个新的数组。然后while 循环会不断执行,直到数组中不再包含任何子数组为止。最终返回完全扁平化的数组。

通过结合解构和 some 方法,可以简洁地实现数组的扁平化。这种方法简单易懂,适用于大多数场景。然而,对于非常大的数组,频繁创建新数组可能会导致性能问题。在这种情况下,可以考虑使用其他方法来优化性能。

toString() + split()

js 复制代码
const arr = [1, 'abc', [3, 4, [5]]];

// 使用 toString 方法将数组转换为字符串
let str = arr.toString();

// 使用 split 方法将字符串按逗号分隔成数组,并将每个元素转换为数字
const newArr = str.split(',').map(item => {
    return Number(item);
});

console.log(newArr); // 输出: [1, NaN, 3, 4, 5]

map() 方法结合 Number() 函数:遍历新数组中的每个元素,并使用 Number() 函数将其转换为数字。这会将数组中的数字保留,而非数字部分转换为 NaN(不是一个数字)。

因此,如果想要过滤掉非数字元素,可以用isNaN()判断这个数是否是数字,同时用filter()过滤掉,如下:

js 复制代码
const arr = [1, 'abc', [3, [4, 5]]];
const str = arr.join();

const newArr = str.split(',')
                  .filter(item => !isNaN(item)) // 过滤掉非数字部分
                  .map(item => Number(item)); // 转换为数字

console.log(newArr); // 输出: [1, 3, 4, 5]

这个方法可以看出它的优点便是,简单易懂且直接,不需要递归或循环等操作,但是它只适用于数组元素是数字或者可以转为字符串这种情况,所以可能相比其他方法,有了更多的限制。

总结

今天呆同学给大家分享几种数组扁平化的几种方法,每种方法都有自己的优点和适用场景,选择合适的方法取决于具体的需求和代码环境。通过掌握这些方法,我们能够更灵活地处理各种类型的嵌套数组,让代码更加简洁高效。那么未来面试时,遇到这个问题,你应该不用慌了吧。

未来如果有更好的数组扁平化方法,期待跟大家一起分享,希望点赞收藏+关注!

相关推荐
码事漫谈8 分钟前
解决 Anki 启动器下载错误的完整指南
前端
im_AMBER27 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
聪明的笨猪猪1 小时前
Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
蓝胖子的多啦A梦1 小时前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码1 小时前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.1 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
聪明的笨猪猪2 小时前
Java Redis “运维”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
苏打水com2 小时前
JavaScript 面试题标准答案模板(对应前文核心考点)
javascript·面试