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]

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

总结

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

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

相关推荐
神夜大侠1 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱1 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号2 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72932 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲2 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
究极无敌暴龙战神X2 小时前
前端学习之ES6+
开发语言·javascript·ecmascript
王解2 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里2 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱2 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster2 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python