JS——扁平化
# 1. 前言
相信我们在刷算法时,都做过把多维数组变成一维数组, 我们有很多办法,例如递归、flat()函数、正则。既然数组可以扁平化为一维数组,那如果我们想把对象扁平化该怎么做呢?那我们就来介绍几种方法去实现数组和对象的扁平化。
# 2. 数组扁平化
题目描述: 给定你一个多维数组,例如:[ 1, 2, [3, 4, [5]], 6, {a: 7} ],让我们将多维数组变为一维数组。
[ 1, 2, [3, 4, [5]], 6, {a: 7} ] ---> [ 1, 2, 3, 4, 5, 6, { a: 7 }
# 2.1 flat()方法
首先我们第一个想到的就 ES10 新增的flat()
方法, 它可以帮助我们实现数组扁平化。实现如下:
const arr = [1, 2, [3, 4, [5]], 6, { a: 7 }];
const newArr = arr.flat(1); // [ 1, 2, 3, 4, [ 5 ], 6, { a: 7 } ]
console.log(newArr);
const newArr1 = arr.flat(2); // [ 1, 2, 3, 4, 5, 6, { a: 7 } ]
console.log(newArr1);
const newArr2 = arr.flat(3); // [ 1, 2, 3, 4, 5, 6, { a: 7 } ]
console.log(newArr2);
flat()
方法,可以直接帮我们实数组的扁平化,而且falt()
方法还接受一个参数供我们选择将数组变为我们想要的维数。
# 2.2 reduce() 方法+递归
那如果没有flat()
方法, 我们还有其他的方法实现了数组扁平化吗?这是,我们还有一种很容易想到的办法,那就是递归。那我们就是使用reduce() 方法+递归
的方式实现吧。
const arr = [1, 2, [3, 4, [5]], 6, { a: 7 }];
let myFlat = (arr) => {
return arr.reduce((pre, next) => {
return pre.concat(Array.isArray(next) ? myFlat(next) : next);
}, []);
};
let newArr = myFlat(arr);
console.log(newArr); // [ 1, 2, 3, 4, 5, 6, { a: 7 } ]
在reduce() 方法+递归
方式里,我们需要想到的就是,当我们循环碰到是数组的话,我们就需要递归的执行,然后将我们递归的结果在拼接成一个新的以为数组。
小提示:如果不熟系这两种方法的小伙伴可以去看# 数组各种好用 API 的手写 (opens new window)文章。
# 2.3 不使用递归
那到这里,很多小伙伴就该问了,使用了递归的性能还是不够好,那有不使用递归的方法吗?那到这里,我就再来实现一个不使用递归的方法。
const arr = [1, 2, [3, 4, [5]], 6, { a: 7 }];
let myFlat1 = (arr, num = 1) => {
let newArr = arr;
let i = 0;
while (arr.some((item) => Array.isArray(item)) && i < num) {
newArr = [].concat(...newArr);
i++;
}
return newArr;
};
let newArr1 = myFlat1(arr, 2);
console.log(newArr1); // [ 1, 2, 3, 4, 5, 6, { a: 7 } ]
我们自己利用了数组身上的some()
方法,去将遍历数组每个元素,再将符合条件的元素结构出来进行拼接,我们也实现了数组的扁平化。
小提示:这里我们还可以想到了一个办法,那就是利用正则表达式,去除数组左右两边的数组再进行拼接,小伙伴可以自己去试试哦
# 3. 对象扁平化
在介绍完数组的扁平化之后,我们就该到我们的对象扁平化了。那我们该如何实现对象的扁平化呢?
题目描述:
const obj = {
a: {
b: 1,
c: 2,
d: { e: 5 }
}
b: [1, 3, { a: 2, b: 3 }]
c: 3
}
----->
// {
// a.b: 1,
// a.c: 2,
// a.d.e: 5
// b[0]: 1,
// b[1]: 3,
// b[2].a: 2,
// b[2].b: 3,
// c: 3
// }
在看数组扁平化之后,我们不难看出实现扁平化的核心原理,就是当我们在循环数组时碰到是数组,我们该怎么处理?我们做的是将数组里的值拿出来,再拼接起来。
那如果是对象的扁平化呢?对象的键该怎么处理呢?那我就先来实现一下吧。
const obj = {
a: {
b: 1,
c: 2,
d: { e: 5 },
},
b: [1, 3, { a: 2, b: 3 }],
c: 3,
};
function flatten(obj) {
let res = {};
function dfs(cur, prefix) {
// prefix参数是为了拿到保留上一级的键
if (typeof cur === "object" && cur !== null) {
// 递归结束条件
if (Array.isArray(cur)) {
// 拿到数组的拼接方式
cur.forEach((item, index) => {
dfs(item, `${prefix}[${index}]`);
});
} else {
for (let key in cur) {
// 拿到对象的拼接方式
dfs(cur[key], `${prefix}${prefix ? "." : ""}${key}`); // 将上一级的键与这一级的键进行拼接
}
}
} else {
res[prefix] = cur;
}
}
// 第一次
dfs(obj, "");
return res;
}
console.log(flatten(obj));
// { // 'a.b': 1, // 'a.c': 2, // 'a.d.e': 5, // 'b[0]': 1, // 'b[1]': 3, // 'b[2].a': 2, // 'b[2].b': 3, // c: 3 // }
在对象的扁平化中,需要拿到对象的键,将对象的键进行一个拼接,然后我们再给对象的键去赋值既实现了对象的扁平化。