数组扁平化,你会几种
前言
数组扁平化,作为一个常见的面试题,经常会被面试官用来考验我们对于 JS 的掌握水平
数组的扁平化是指将多层嵌套的数组结构(嵌套可以是任何层数)转换为只有一层的数组。在 JavaScript 中,有多种方法可以实现数组扁平化。
本文将介绍六种常见的实现方法,并对它们进行总结和比较。
实现
普通递归实现
使用递归方式遍历数组中的每个元素,如果当前元素是数组,则递归调用该函数将其展平,否则将当前元素添加到结果数组中。最终返回展平后的结果数组
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.push(arr[i])
}
}
return result
}
let arr = [1, 2, [3, 4], [5, [6, 7]]]
console.log(flatten(arr))// [1, 2, 3, 4, 5, 6, 7]
在这个例子中,flatten
函数接受一个数组参数 arr
,并返回一个扁平化后的数组。在遍历数组元素时,如果当前元素是数组,则递归调用 flatten
函数将其展开,并使用 Array.prototype.concat
方法将展开后的数组与结果数组合并;否则将当前元素添加到结果数组中。最终返回结果数组。
用 reduce 方法迭代
从上面的普通递归中可以看出,其实就是对数组的每一项进行处理,我们这里用到reduce函数来实现数组的拼接,从而简化第一种代码
函数接受一个数组 arr
作为参数,并使用 reduce()
方法对数组进行迭代。reduce()
方法接受一个回调函数和一个初始值(这里是空数组 []
)作为参数。
回调函数中的逻辑如下:
- 对于每个数组元素
cur
,检查它是否是数组,使用Array.isArray()
方法进行判断。 - 如果
cur
是数组,那么递归调用flatten(cur)
,继续展开嵌套的数组。 - 如果
cur
不是数组,表示是一个非嵌套的元素,直接将其添加到结果数组中。 - 使用
concat()
方法将展开后的数组或非嵌套元素连接到结果数组pre
中。
function flatten(arr) {
return arr.reduce((pre, cur) => {
return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, []);
}
let arr = [1, 2, [3, 4], [5, [6, 7]]]
console.log(flatten(arr))// [1, 2, 3, 4, 5, 6, 7]
扩展运算符实现
使用了循环结构和拓展运算符 ...
来遍历数组中的每个元素,如果当前元素是数组,则使用 concat
方法将其展开并将结果赋值给原数组,直到数组中不再包含嵌套数组为止。
function flatten(arr) {
while (arr.some(Array.isArray)) {
arr = [].concat(...arr);
}
return arr
}
let arr = [1, 2, [3, 4], [5, [6, 7]]]
console.log(flatten(arr))// [1, 2, 3, 4, 5, 6, 7]
在这个示例中,flatten
函数接受一个数组参数 arr
,并不返回任何值。在循环结构中,使用 some
方法判断数组中是否还存在嵌套数组,如果存在,则使用 concat
方法将嵌套数组展开,并使用拓展运算符 ...
将展开后的数组合并。这个过程会不断重复,直到数组中不再包含嵌套数组。
concat
方法创建一个新数组。该数组将首先由调用它的对象中的元素填充。然后,对于每个参数,它的值将被连接到数组中——对于普通对象或基元,参数本身将成为最终数组的一个元素;对于属性Symbol.isConcatSpreadable设置为真的数组或类数组对象,参数的每个元素都将是独立地添加到最终数组中。详情可查看MDN concat简单来说就是,如果是普通对象或者基础数据类型,就直接添加进新数组;如果是数组或者类数组对象,那就是将数组和类数组对象的元素添加进新数组
[].concat(1, [2, 3])// [1, 2, 3]
在计算机程序设计中,基元(primitive)是指最基本的数据类型,也称为原始数据类型(primitive data type)。在这里自然指的是数字类型,字符串类型,布尔类型等基础数据类型
前三种实现数组扁平化的方式都是最基本的思路,都是通过最普通递归思路衍生出来的方法
ES6的flat
直接调用 ES6 的 flat
,可以直接实现数组扁平化
array.flat([depth])
depth
(可选):指定要递归扁平化的嵌套层数,默认为 1。如果传递一个大于 0 的整数,表示递归扁平化的层数;如果传递 Infinity,则会完全扁平化所有嵌套的子数组
let arr = [1, 2, [3, 4], [5, [6, 7]]]
console.log(arr.flat(Infinity))// [1, 2, 3, 4, 5, 6, 7]
flat
方法的内部实现是使用迭代的方式来展平数组,其时间复杂度为 O(n),空间复杂度为 O(n)。在执行
flat
方法时,它会遍历原数组中的每个元素,并判断该元素是否为数组。如果是数组,则将其中的元素> 追加到展平后的结果数组中;否则直接将该元素追加到结果数组中。这样,就可以将嵌套数组展平成一个一维数组。在展平的过程中,
flat
方法会创建一个新的数组来存储展平后的结果。因此,展平后的结果数组的长度和原数组中所有元素的数量一样。这就是空间复杂度为 O(n) 的原因。
以上四个flatten方法都是相同输出
const arr1 = [1, [2, [3, [4, 5]]], {obj: 1}, function aaa() {}, new RegExp(), new Date(), null, undefined, '2', false, ];
toString和split共同处理
function flatten(arr) {
return arr.toString().split(',')
}
var arr1 = [1, [2, [3, [4, 5]]], {obj: 1}, function aaa() {}, new RegExp(), new Date(), null, undefined, '2', false, ];
arr.toString().split(',')
的作用是将数组 arr
转换为一个字符串,然后使用逗号分隔符将字符串拆分为一个字符串数组。具体来说,它使用了 Array.prototype.toString()
方法将数组转换为一个字符串,然后使用 String.prototype.split()
方法将字符串按照逗号分隔符拆分为一个字符串数组。这个方法常用于将数组展开为一组逗号分隔的值,或者将一个字符串按照逗号分隔符拆分为一个字符串数组。
这种方法的优点是代码简洁,易于理解和实现。但缺点也显而易见
- 不能处理数组中包含逗号的情况。因为逗号会被误认为是分隔符,导致数组元素无法正确地转换为字符串
- 不能处理数组中包含
undefined、null、NaN
等特殊值的情况,因为这些值在转换为字符串时会变成空字符串或字符串
6. 正则和 JSON 方法结合
通过将数组转换为字符串,去除其中的方括号,然后重新构造为一个数组字符串,并最后将其解析为数组。
由于使用到了 JSON.stringify() 方法,所以无法对 函数
、 undefined
、symbol
、Date引用类型
等类型正确转换
function flatten(arr) {
let str = JSON.stringify(arr)
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
return JSON.parse(str);
}
const arr1 = [1, [2, [3, [4, 5]]], {obj: 1}, function aaa() {}, new RegExp(), new Date(), null, undefined, '2', false, ];
console.log(flatten(arr1))
总结
数组扁平化是一项常见的任务,可以通过多种方法实现。上面介绍了六种实现数组扁平化的常见方法,也说明了各自的优缺点,希望大家能够更好的了解Javascript和面试的时候能够用得上
至于开发过程中,还是用现成的就好,毕竟开发工作中效率第一嘛
最后,祝大家变得更强!
转载自:https://juejin.cn/post/7268230802856542243