JS通过递归,处理树型结构数据

Posted Dormiveglia-flx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS通过递归,处理树型结构数据相关的知识,希望对你有一定的参考价值。

JS通过递归,处理树型结构数据

为什么要将扁平化的数据,变成树型结构?

// 比如这样的数据,后台给的是扁平化数据,前端需要制作成树型结构,供vue-router使用
const routeData = [
		    
			    id:1,
				pid:0,
				name:'菜单subMenu-1'
			,
                id:2,
				pid:0,
				name:'菜单subMenu-2'
			,
			  id:3,
			  pid:1,
			  name:'SBM-1-Menu-1'
			
		]

先从一个简单的例子开始

现在的数据只有两级:

最终的树型结构是:

       菜单subMenu-1
               SBM-1-Menu-1
               SBM-1-Menu-2
       菜单subMenu-2
               SBM-2-Menu-1
               SBM-2-Menu-2
思路:
   1.
   找到顶级的item元素(pid==0),放到数组parents中。
   找到剩余的item元素(pid!=0),放到数组children中。

   2.
   遍历两个数组。当顶级的id == 子级的pid的时候。给顶级添加children,并把子级push进去。
		const routeData = [
		    
			    id:1,
				pid:0,
				name:'菜单subMenu-1'
			,
                id:2,
				pid:0,
				name:'菜单subMenu-2'
			,
			  id:3,
			  pid:1,
			  name:'SBM-1-Menu-1'
			,
			  id:4,
			  pid:1,
			  name:'SBM-1-Menu-2'
			,
			
			  id:5,
			  pid:2,
			  name:'SBM-2-Menu-1'
			,
			
			  id:6,
			  pid:2,
			  name:'SBM-2-Menu-2'
			
		]



        
        function ArrayToTree(routeData)
		   // 1 先找出顶级的元素,把顶级元素放数组里面
		   const parents = routeData.filter(item=>
						return item.pid==0;
						)
		   
		   // 2 把第二级元素组成数组.
		   //然后顶级数组与第二级数组一起遍历。找到顶级的id===子级的pid
		   const children = routeData.filter(item=>
						return item.pid!=0;
						)
						
		   //console.log(children)
           parents.map(pitem=>
		       children.map(citem=>
			       // 子级的pid===顶级的id
			       if(citem.pid==pitem.id)
				      // 没有children就创建一个,有的话就直接push
				      if(pitem.children)
					    pitem.children.push(citem)
					  else
					     pitem.children = [citem]
					  
				   
			   )
		   )
		   
		   // 使用map,会返回一个新的数组,而不改变原数组
		   //console.log(routeData)
		   //console.log(parents)
		   return parents
		
		
		ArrayToTree(routeData)



// 这个时候,一个树型结构就出现了。

进入主题:
如果将来菜单的级数特别多怎么办?

       菜单subMenu-1
               SBM-1-Menu-1
                     SBM-1-Menu-1-1
                           SBM-1-Menu-1-1-1
               SBM-1-Menu-2
       菜单subMenu-2
               SBM-2-Menu-1
                    SBM-2-Menu-1-1
               SBM-2-Menu-2
      菜单subMenu-3

这个时候,也是需要 遍历两个数组。当顶级的id == 子级的pid的时候,添加children。也就是说顶级的id == 子级的pid,添加children并push 需要执行多遍,且不知道具体执行多少遍(不知道有几级)的时候,可以用递归

		// 那如果有无限制级子级数据,应该怎么做? -----递归
		const routeData2 = [
		    
			    id:1,
				pid:0,
				name:'菜单subMenu-1'
			,
                id:2,
				pid:0,
				name:'菜单subMenu-2'
			,
			  id:3,
			  pid:1,
			  name:'SBM-1-Menu-1'
			,
			  id:4,
			  pid:1,
			  name:'SBM-1-Menu-2'
			,
			
			  id:5,
			  pid:2,
			  name:'SBM-2-Menu-1'
			,
			
			  id:6,
			  pid:2,
			  name:'SBM-2-Menu-2'
			,
			  id:7,
			  pid:3,
			  name:'SBM-1-Menu-1-1'
			,
			  id:8,
			  pid:3,
			  name:'SBM-1-Menu-1-2'
			,
			  id:9,
			  pid:7,
			  name:'SBM-1-Menu-1-1-1'
			,
			  id:10,
			  pid:8,
			  name:'SBM-1-Menu-1-2-1'
			
		]


         // 按照刚才分配两个的思路,递归的话,主要是递归 id==citem.pid
		function ArrayToTree2(routeData)
		    // 1 先找出顶级的元素,把顶级元素放数组里面
		    const parents = routeData.filter((item)=>
		      return item.pid==0
		   )
		   
		   // 2 把第二级元素组成数组.
		   //然后顶级数组与第二级数组一起遍历。找到顶级的id===子级的pid
		   const children = routeData.filter(item=>
			  return item.pid!=0;
		   )
		   
		   Findchildren(parents,children)
		


		function Findchildren(parents,children)						
           parents.map(pitem=>
		       children.map((citem,index)=>
			       // 子级的pid===顶级的id
			       if(citem.pid==pitem.id)
				   
				      const myChildren = JSON.parse(JSON.stringify(children))
					  //myChildren.splice(index,1)    优化一下
			          Findchildren([citem],myChildren)
				   
				      // 没有children就创建一个,有的话就直接push
				      if(pitem.children)
					    pitem.children.push(citem)
					  else
					     pitem.children = [citem]
					  
				   
			   )
		   )
		   console.log(parents)
		


        ArrayToTree2(routeData2)
 // 在这里,递归可以理解成代码的嵌套,每次遇到 Findchildren ,就把这个给换成代码体里面的内容。也就是当有多级的时候,就会有多层嵌套,比如:
         
        parents.map(pitem=>
            children.map(citem=>
                 
                 parents.map(pitem=>
                      children.map(citem=>

                           parents.map(pitem=>
                                children.map(citem=>

                                   ...里面是函数体的内容,传的参数不一样

                                )
                           )
                        
                      )
                 )
                 
            )
        )

sqlite 树型结构查询


表结构:tblCity(ID, ParentID, Name)

因为sqlite 没有row_number函数,也不能递归查询,所幸它有RowID 这个字段。只好采用这种 笨方法


1)

select ID,Name,1 as Level from tblCity where ParentID=0
union all
select a.ID,a.Name,c.RowID as Level from tblCity a
inner join tblCity b on a.ParentID=b.ID
inner join
(
select ParentID from tblCity group by ParentID
) c on a.ParentID=c.ParentID


2)

select ID,Name,1 as Level from tblCity where ParentID=0
union all
select a.ID,a.Name,c.RowID as Level from tblCity a
inner join tblCity b on a.ParentID=b.ID
inner join
(
select ParentID from tblCity group by ParentID
) c on a.ParentID=c.ParentID



以上是关于JS通过递归,处理树型结构数据的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE 递归树型结构统计汇总

想用java实现一个无限级树型菜单,数据库是这样设计的:id,pid(父id),name.用递归调用,要一次性加载.

js中平级数组和树形结构数据相互转换

JS中的二叉树遍历

sqlite 树型结构查询

通过简单的AJAX递归请求进行多任务的数据自动处理