# 树状数组结构转化

# 题目如下:

这道题是我朋友发给我的,之前一开始看的时候,觉得很简单,但仔细往下看的时候,眉头一皱发现事情并不简单。

PS:这类题目之前也以不同形式出现过

# 传进去的数组:

let oldArr = [
	{
		'1_class': '工具',
		'2_class': '备忘录',
		'1_id': 1,
		'2_id': 2
	},
	{
		'1_class': '教育',
		'2_class': '学历教育',
		'3_class': '中等',
		'1_id': 3,
		'2_id': 4,
		'3_id': 6
	},
	{
		'1_class': '教育',
		'2_class': '学历教育',
		'3_class': '高等',
		'1_id': 3,
		'2_id': 4,
		'3_id': 5
	},
	{
		'1_class': '教育',
		'2_class': '成人教育',
		'1_id': 3,
		'2_id': 7
	}
]
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

# 输出的数组:

let result = [
	{
		value: 1,
		label: '工具',
		children: [
			{
				value: 2,
				label: '备忘录',
				children: []
			}
		]
	},
	{
		value: 3,
		label: '教育',
		children: [
			{
				value: 4,
				label: '学历教育',
				children: [
					{
						value: 6,
						label: '中等',
						children: []
					},
					{
						value: 5,
						label: '高等',
						children: []
					}
				]
			},
			{
				value: 7,
				label: '成人教育',
				children: []
			}
		]
	}
]
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

# 建议大家好好想想,争取能够自己解出来

# 参考一下我的解题方法:

  1. 先算出层级嵌套数

    遍历数组,再遍历数组的元素,用parseInt拿到每一个值,将最大的值取出来,即为层级嵌套数。

  2. 数组转成属性层级的对象。对象的属性是层级,值是数组,层级里面的值。

    1.去重操作(比如:1_id相等的话就不再重复添加)

    2.提取需要的信息,最终的值和相应父级的信息(之后组装数组的时候放到对应的地方,需要的信息)。

    最后输出的对象是这个样子

  1. 将属性层级的对象转成树状结构数组。

    遍历对象,获取层级值。

    遍历层级的每个元素。

    找到对应的层级将值添加进去,否则递归继续找对应层级。

# 代码:

let listToTree = arr => {
	let [levelNum, newArr, obj] = [1, [], {}]
	// 层级嵌套数
	arr.forEach(item => {
		for (let proto in item) {
			let protoLevel = parseInt(proto)
			if (levelNum < protoLevel) levelNum = protoLevel // 总共多少层级
		}
	})
	// 分开层级 放在一个对象中
	for (let i = 1; i < levelNum + 1; i++) {
		levelClass(i)
	}
	// 将对象转化成数组
	Object.keys(obj).forEach(item => {
		let forNum = parseInt(item) // 当前层级数
		obj[item].forEach(itemChildren => {
			// 遍历每个层级的每个值
			packageArr(itemChildren, forNum, newArr)
		})
	})
	// 分开每个层级
	function levelClass(name) {
		arr.forEach(value => {
			// 每个元素都遍历一次 分开对应层级
			if (!value[`${name}_id`]) return // 有的对象层级没那么多
			let objFor = {}
			// 其他层级的value 赋值
			for (let j = 1; j < name; j++) {
				objFor[`value${j}`] = value[`${j}_id`]
			}
			if (obj[`${name}_id`]) {
				// 相同层级不重复添加 比如第一层级 id都为1 只添加一个
				let status = obj[`${name}_id`].find(item => {
					return item.value === value[`${name}_id`]
				})
				// 没有才添加
				if (!status) {
					obj[`${name}_id`].push(
						Object.assign(objFor, {
							value: value[`${name}_id`],
							label: value[`${name}_class`]
						})
					)
				}
			} else {
				// 初始化创建一个数组
				obj[`${name}_id`] = [
					Object.assign(objFor, {
						value: value[`${name}_id`],
						label: value[`${name}_class`]
					})
				]
			}
		})
	}
	// 组装每个值
	function packageArr(sureName, index, arr, key = 1) {
		if (key === index) {
			// 当key和index相同时 即找到当前层级
			return arr.push({
				value: sureName.value,
				label: sureName.label,
				children: []
			})
		} else {
			key++
			// 当前层级数组中对应的对象元素
			let num = arr.findIndex(value => {
				return value.value === sureName[`value${key - 1}`]
			})
			// 继续找或者已经找到
			return packageArr(sureName, index, arr[num].children, key)
		}
	}
	return newArr
}
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# 更好的解决方式:只关注层级有没有被组装过

感谢MrHouBeiBei提供更好的解决方法:

function getNewArr(activeArr) {
	// 递归处理传进来的对象
	function fn(arr, obj, id) {
		var objLen = Object.keys(obj).length / 2 // 对象总共的层级
		var rtItem = arr.find(i => {
			return i.value === obj[`${id}_id`] // 找该层级是否被组装过
		})
		// 没被组装过 就组装
		// 被组装就跳过(覆盖)
		if (!rtItem) {
			rtItem = {}
			rtItem.value = obj[`${id}_id`] // 层级id
			rtItem.lable = obj[`${id}_class`]
			rtItem.children = []
			arr.push(rtItem)
		}
		id++ // 准备组装下一层级
		// 如果层级结束就退出递归
		if (id > objLen) return
		// 递归继续组装
		fn(rtItem.children, obj, id)
	}

	var res = []
	for (let item of activeArr) {
		// 遍历数组 处理每个对象
		var id = 1
		fn(res, item, id)
	}
	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
26
27
28
29
30
31