扁平数据转为树形数据

231 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

前几天面试题中遇到一个题是将普通数据转为树形数据,我觉得考察这个知识点还是很有用的,毕竟在工作中我们经常会根据后端返回的数据进行处理,其中转化树形数据尤为常见,所以对该知识点再进行复习整理下。

1.使用原生方式转为树形数据并渲染到页面

源码如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>普通数据转为树形数据</title>
		<style type="text/css">
			*{
				margin: 0;
			}
			.box{
				box-sizing: border-box;
				width: 500px;
				height: 100%;
				margin: 10px auto;
				padding: 20px;
				border: 1px solid hotpink;
				
			}
			li{
				margin-bottom: 10px;
				font-size: 13px;
			}
		</style>
	</head>
	<body>
		<div class="box"></div>
        <script type="text/javascript">
            let list = [
                { id: 21, title: '海南省', pid: 0 },
                { id: 268, title: '海口市', pid: 21 },
                { id: 269, title: '三亚市', pid: 21 },
                { id: 270, title: '三沙市', pid: 21 },
                { id: 271, title: '儋州市', pid: 21 },
                { id: 22, title: '重庆市', pid: 0 },
                { id: 23, title: '四川省', pid: 0 },
                { id: 288, title: '成都市', pid: 23 },
                { id: 289, title: '自贡市', pid: 23 },
                { id: 290, title: '攀枝花市', pid: 23 },
                { id: 291, title: '泸州市', pid: 23 },
                { id: 292, title: '德阳市', pid: 23 },
                { id: 293, title: '绵阳市', pid: 23 },
                { id: 2436, title: '锦江区', pid: 288 },
                { id: 2437, title: '青羊区', pid: 288 },
                { id: 2438, title: '金牛区', pid: 288 },
                { id: 2439, title: '武侯区', pid: 288 },
                { id: 2440, title: '成华区', pid: 288 },
            ]
            // 定义一个空字符串用来存渲染的字符串
            let menus = '';
            function getTreeData(id, array){
                let childArray = getParent(id, array);
                if(childArray.length > 0){
                    menus += 	`<ul>`;
                    for(let i in childArray){
                        menus += '<li>' + childArray[i].title;
                        getTreeData(childArray[i].id, array);
                        menus += '</li>';
                    }
                    menus += '</ul>';
                }
            }

            // 定义获取父级菜单的方法(一级为二级的父级, 二级为三级的父级)
            function getParent(id, array){
                let arr = [];
                for(let i in array){
                    if(array[i].pid === id){
                        arr.push(array[i])
                    }
                }
                return arr;
            }
            let oBox = document.getElementsByTagName('div')[0];
            getTreeData(0, list)
            oBox.innerHTML = menus
        </script>
	</body>
</html>

渲染结果

树形数据1.png

主要是利用递归的方式来实现树形结构转换

2.结合 ui 组件实现

这里使用的是 iview 中的 Tree 组件, App.vue 代码如下:

<template>
    <div id="app">
        <h3>树形数据展示</h3>
        <Tree :data="treeData"></Tree>
    </div>
</template>

<script>
export default {
        name: 'App',
        data() {
            return {
                list: [
                    { id: 21, title: '海南省', pid: 0 },
                    { id: 268, title: '海口市', pid: 21 },
                    { id: 269, title: '三亚市', pid: 21 },
                    { id: 270, title: '三沙市', pid: 21 },
                    { id: 271, title: '儋州市', pid: 21 },
                    { id: 22, title: '重庆市', pid: 0 },
                    { id: 23, title: '四川省', pid: 0 },
                    { id: 288, title: '成都市', pid: 23 },
                    { id: 289, title: '自贡市', pid: 23 },
                    { id: 290, title: '攀枝花市', pid: 23 },
                    { id: 291, title: '泸州市', pid: 23 },
                    { id: 292, title: '德阳市', pid: 23 },
                    { id: 293, title: '绵阳市', pid: 23 },
                    { id: 2436, title: '锦江区', pid: 288 },
                    { id: 2437, title: '青羊区', pid: 288 },
                    { id: 2438, title: '金牛区', pid: 288 },
                    { id: 2439, title: '武侯区', pid: 288 },
                    { id: 2440, title: '成华区', pid: 288 },
                ],
            }
        },
        computed: {
            // 方法1: 直接遍历list数组,给每项添加children,通过筛选 pid 和 id值是否相等来添加值
            treeData() {
                let newList = JSON.parse(JSON.stringify(this.list))
                let result = newList.map(item=>{
                        item.children = newList.filter(val=>val.pid === item.id)
                        return item
                })
                // 最后筛选出 pid 为 0 的项
                return result.filter(item=>item.pid === 0)
            }

            /*
                方法2:遍历数组,使用 item.id 和 item作为对象的键值对进行标记,添加children属性,判断当前项的 pid 是否为0,为0就添加到 result 数组中,不为0就添加到 item.pid 对应对象的 children中
            */ 
            // treeData() {
            // 	let newList = JSON.parse(JSON.stringify(this.list))
            // 	let result = [];
            // 	let obj = {};
            // 	for (let item of newList) {
            // 		obj[item.id] = item;
            // 		obj[item.id].children = [];
            // 		if (item.pid === 0) {
            // 			result.push(obj[item.id])
            // 		} else {
            // 			obj[item.pid].children.push(item)
            // 		}
            // 	}
            // 	return result
            // }
            
            /* 方法3: 使用递归获取子集 */
            // treeData(){
            // 	let newList = JSON.parse(JSON.stringify(this.list))
            // 	return this.toTreeData(newList, 0)
            // }
        },
        methods:{
            toTreeData(arr, pid){
                    let result = [];
                    this.getChildren(arr, result, pid)
                    return result
            },
            getChildren(data, result, pid){
                    for(let item of data){
                            if(item.pid === pid){
                                    let newItem = {...item, children:[]}
                                    result.push(newItem)
                                    this.getChildren(data, newItem.children, item.id)
                            }
                    }
            }
        }
}
</script>

<style>
	#app {
		width: 500px;
		height: 100%;
		border: 1px solid red;
		margin: 10px auto;
	}
	
	#app h3 {
		width: 100%;
		text-align: center;
	}
</style>

树形数据2.png

有个小缺陷是筛选后的数据作为 Tree 组件的值,页面渲染出来的数据默认是没有展开的,需要设置它的 expand 属性并通过判断当前项的 children 是否有值来添加

参考: juejin.cn/post/698390…