# 前言
有时候后台返回前端的数据结构嵌套层数过深,访问里面的属性的时候过于复杂,于是乎就有了深层数组及对象的扁平化方式。
# 数组扁平化(javascript)
# 递归:
「步骤:」
创建一个空的新数组;
遍历多维数组;
如果当前元素是非数组,将元素添加到新数组末尾;
如果当前元素是数组,元素调用函数本身;
返回新数组。
function flatten(array, result) {
result = result || [];
array.forEach(element => {
if (Array.isArray(element)) {
flatten(element, result);
} else {
result.push(element);
}
});
return result;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 非递归
「步骤:」
创建一个空的新数组;
创建一个队列;
循环遍历队列,每次移除首个元素;
如果首个元素是非数组,将首个元素添加到新数组开头;
如果首个元素是数组,将首个元素添加到队列的末尾;
返回新数组。
function flat(array){
let arr = [];
let queue = array;
if(!queue.length){
return [];
}
while(queue.length){
let val = queue.pop();
if(Array.isArray(val)){
queue.push(...val);
}else{
arr.unshift(val);
}
}
return arr;
}
flat(arr);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# underscore实现源码:
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, strict, output) {
output = output || [];
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
// Flatten current level of array or arguments object.
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 测试
var arr = [2, [3, 4],
[
[5],
[6]
]
];
var result = flatten(arr);
console.log(result);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 对象扁平化(javascript)
# 递归
「步骤:」
创建一个空新对象;
遍历对象属性;
如果当前属性是基本类型,将属性及值复制到新对象上;
如果当前属性是引用类型,将属性及值递归调用函数本身;
返回新对象。
function flat(obj, key = "", res = {}, isArray = false) {
for (let [k, v] of Object.entries(obj)) {
if (Array.isArray(v)) {
let tmp = isArray ? key + "[" + k + "]" : key + k
flat(v, tmp, res, true)
} else if (typeof v === "object") {
let tmp = isArray ? key + "[" + k + "]." : key + k + "."
flat(v, tmp, res)
} else {
let tmp = isArray ? key + "[" + k + "]" : key + k
res[tmp] = v
}
}
return res
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 非递归
「步骤:」
创建一个空新对象;
创建一个队列;
循环遍历队列,每次移除首个元素键值对数组;
如果首个元素键值对数组的值是基本类型,将键及值复制到新对象上;
如果首个元素键值对数组的值是引用类型,将键及值添加到队列的末尾;
返回新对象。
/**
* 使用队列或栈 扁平数组
* @param {*} object 树形数据
* @return {target}
*/
function flat(object){
let res = {};
let queue = Object.entries(object);
while(queue.length){
// 栈 FILO Array.pop() 删除数组的最后一个元素并返回删除的元素
// const [key, obj] = queue.pop();
// 队列 FIFO Array.shift() 删除并返回数组的第一个元素。
let [key, obj] = queue.shift();
Object.entries(obj).forEach(item=>{
let [k, v] = item;
if(typeof v!= 'object'){
res[`${key}.${k}`] = v;
}else{
// Array.shift() 向数组的末尾添加一个或更多元素,并返回新的长度。
queue.push([`${key}.${k}`, v]);
}
});
}
return res;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 测试
let object =
{
"a": {
"b": {
"c": {
"d": "qdkabcd"
}
},
"d": {
"x": [
{
"d": "qdkabcd"
},
{
"f": "qdkabcd"
}
]
},
"e": "qdkae"
}
}
var result = flattenObj(entryObj);
console.log(result);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 数组扁平化(typescript)
# 代码实现
flatten(array: any[], result: any[]): Array<any> {
result = result || [];
array.forEach((element) => {
if (Array.isArray(element)) {
this.flatten(element, result);
} else {
result.push(element);
}
});
return result;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 对象扁平化(typescript)
# 代码实现
flat(obj: Object, key = '', res: { [x: string]: unknown }, isArray = false) {
for (let [k, v] of Object.entries(obj)) {
if (Array.isArray(v)) {
let tmp = isArray ? key + '[' + k + ']' : key + k;
this.flat(v, tmp, res, true);
} else if (typeof v === 'object') {
let tmp = isArray ? key + '[' + k + '].' : key + k + '.';
this.flat(v, tmp, res);
} else {
let tmp = isArray ? key + '[' + k + ']' : key + k;
res[tmp] = v;
}
}
return res;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 数组对象扁平化(typescript)
# 代码实现
let arr = [
{
a: {
b: {
c: {
dd: 'abcdd',
},
},
d: {
xx: 'adxx',
},
e: 'ae',
},
},
{
a: {
b: {
c: {
dd: 'abcdd',
},
},
d: {
xx: 'adxx',
},
e: 'ae',
},
},
];
debugger;
// 第一种
let arr2 = arr.map((e) => this.flat(e));
// 第二种
let arr3 = arr.map((e) => {
return this.flat(e);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 反扁平化
function unflatten(obj) {
let o = {}
for (let key in obj) {
transformKey(key, obj[key], o)
}
return o
}
//转化key
function transformKey(key,value,head){
const arr = key.split('.')
let tmp = head
for(let i =0;i<arr.length;i++) {
let key = arr[i]
let nextKey = arr[i + 1];
//这里需要判断key 是否包含[]字符串,如果是则是数组结构
if(/\[.+?\]/g.test(key)){
//可能是多维数组,匹配数组维度
let indexs = key.match(/\[(.+?)\]/g);
//获取数组的key值
let _key = key.match(/^(.+?)\[/)[1];
//构造数组需要判断是否已经存在
tmp[_key] = tmp[_key]?tmp[_key]:[]
let n = tmp[_key]
//构造完数组对数组里面进行构造
for(let j=0;j<indexs.length;j++){
let index = indexs[j].replace(/\[|\]/g, '');
let nextIndex = indexs[j+1]
//数组包含数组
if(nextIndex){
//构造数组需要判断是否已经存在
n[index] = n[index]?n[index]:[]
//如果还包含数组,将n指针指向下一个数组
n = n[index]
}else{
//如果后面还有则构造对象
if (nextKey) {
//构造对象需要判断是否已经存在
n[index] = n[index]?n[index]:{}
tmp = n[index]
} else {
n[index] = value
}
}
}
} else {
//不是数组和之前方法保持一致
if (nextKey) {
//构造对象需要判断是否已经存在
tmp[key] = tmp[key]?tmp[key]:{}
tmp = tmp[key]
} else {
tmp[key] = value
}
}
}
return head
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63