目录
什么是数据扁平化?
JS数据扁平化是指将一个多层嵌套的数组 转换为 一个一维数组的过程。在多层嵌套的数组中,元素可以是基本数据类型,也可以是数组,这种层层嵌套的结构使得数组的处理变得复杂。通过扁平化数组,可以简化对数组的操作和处理。
简单来讲就是:
他是一个数据处理的过程或步骤,通常用于将嵌套的数据结构(如嵌套数组或对象)转换为非嵌套(或“扁平”)结构。这个过程在编程中很常见。
JS数据扁平化的好处主要有以下几点:
- 简化数据结构:在处理递归结构或者进行某些操作时(如遍历、查找、排序等),使用一维数组往往比使用多维数组更为方便和直观。。
- 优化性能:在某些场景下,如前端开发中处理递归的组件结构或嵌套的API响应数据时,使用扁平化后的数组可以提高性能,减少不必要的计算量。
下面是对其好处使用的举例:
就举几个简单的例子吧。
文件目录的查找:
假设你有一个表示目录结构的嵌套数组,每个目录可能包含子目录和文件。如果你需要遍历所有文件或进行某种全局搜索,嵌套数组的结构可能会让这个过程变得复杂。通过将嵌套数组扁平化为一维数组,你可以更容易地访问和操作所有的文件。
下面这个使用了递归,注意的是,对于非常大的数组或多层嵌套的数组,递归可能导致堆栈溢出。
let nestedDirs = [
{ name: '文件目录1', children: [{ name: '文件1' }, { name: '文件2' }] },
{ name: '文件目录2', children: [{ name: '文件目录2-1', children: [{ name: '文件3' }] }] }
];
// 假设这是一个将嵌套目录结构扁平化为文件列表的函数(这里简化处理)
function flattenFiles(dirs) {
let files = [];
dirs.forEach(dir => {
if (dir.children) {
files = files.concat(flattenFiles(dir.children));
} else {
files.push(dir.name);
}
});
return files;
}
let flatFiles = flattenFiles(nestedDirs);
console.log(flatFiles); // ['文件1', '文件2', '文件3']
排序:
当你需要将一个数组中的元素进行排序、去重或进行其他数组操作时,如果数组是嵌套的,那么你可能需要编写更复杂的逻辑来处理这些操作。而扁平化后的数组则可以直接使用JavaScript的内置数组方法来轻松完成这些操作。
Array.prototype.flat
:
(这个方法是非常推荐的)
语法:
flat()
flat(depth)
//depth 可选
//指定要提取嵌套数组的结构深度,默认值为 1。
Array.prototype.flat
是ES6
新增的一个数组方法,它的作用就是用来数组扁平化,并且根据传入的参数来决定展开的层级,Infinity
表示完全展开。如果要展开元素,则它们必须是数组。
flat()
方法创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中。意思就是:
它不会改变
this
数组,而是返回一个浅拷贝,该浅拷贝包含了原始数组中相同的元素。
像这种简单的,其实也可以用 toString()+split(',') 实现,意思就不用说了吧
let nestedArray = [1, [6, 3], [4, [5, 8]], 7];
// 使用 flat 方法进行扁平化
let flatArray = nestedArray.flat(Infinity);
// 对扁平化后的数组进行排序
flatArray.sort((a, b) => a - b);
console.log(flatArray); // [1, 3, 4, 5, 6, 7, 8]
其他方法实现数据扁平化:
扩展运算符 (...
) 和递归
结合扩展运算符和递归来实现扁平化。这种方法在函数式编程中很常见。
function flatten(array) {
return array.reduce((flat, toFlatten) => {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
// 或者使用更简洁的递归方式
function flattenDeep(arr) {
return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
const nestedArray = [1, [2, 3], [4, [5, 6]]];
const flatArray = flattenDeep(nestedArray);
console.log(flatArray); // [1, 2, 3, 4, 5, 6]
使用栈(Stack)
当处理大型嵌套数组时,使用栈可以避免递归调用栈过深的问题。
使用栈(Stack)来实现数组扁平化时,实际上并没有直接展示一个显式的栈数据结构(如 Array
用于模拟栈)
显式的栈或队列数据结构在多种编程语境中都存在,特别是在那些提供标准库或内置数据结构的编程语言中。
在栈的数据结构中,我们通常有两个基本操作:push
(压栈)和pop
(弹栈)。但在数组扁平化的场景中,我们其实是在模拟一个后进先出(LIFO)的队列,因为我们总是从嵌套数组的末尾开始处理,并将内部的数组元素推入“待处理”的数组中。
数组提供了 push
和 pop
方法来模拟栈的操作,但在这个特定的扁平化场景中,我们更多地是使用 concat
、reduce
或扩展运算符来模拟“推入”和“弹出”的行为。
// 创建一个空数组来模拟栈,并将初始数组的元素作为栈的底部元素(实际上由于push操作,它们会成为栈顶元素)
const stack = [1, [2, 3], [4, [5, 6]]]; // 使用扩展运算符将数组元素放入栈中
const result = []; // 用于存储扁平化后的结果
// 当栈不为空时,循环处理
while (stack.length > 0) {
// 弹出栈顶元素(即待处理的数组或值)
const current = stack.pop();
// 判断当前元素是否为数组
if (Array.isArray(current)) {
// 如果是数组,将其元素依次推入栈中(使用push保持后进先出的顺序)
stack.push(...current); // 相当于将数组元素逆序压入栈中,但因为后续pop操作,处理顺序仍然是正确的
} else {
// 如果不是数组,则直接添加到结果数组中
result.unshift(current); // 将非数组元素添加到结果数组中
}
}
// 返回扁平化后的结果数组
console.log(result,'result');
总结:
flat()
方法 通常 是最简单且性能最好的选择。如果你需要处理更复杂的扁平化逻辑(如过滤掉某些元素),那么
reduce()
方法可能更适合。如果你需要处理大型嵌套数组并且担心递归调用栈溢出,那么栈的方法可能是最佳选择。