关于树状结构数据的一些常用处理,比如找所有父级和子级,一维数组转无限级树状结构
树状结构数据在日常开发是最经常遇到的数据,比如一些后台管理系统左侧菜单就是一个树状结构的数据,这些数据的特点有,可以无限的子节点,父级与子级一般会存在上级关系,比如子级的属性会有父级的唯一标识id,我这里总结了,一维数组转无限级树状结构,树状结构转一维数组,根据指定属性值找所有的父级或者子级数据,有不对的地方,还望大佬们多多指点.
一、一维数组转无限级树状结构
1、 使用递归法: 数据会存在上下级关系,否则无法转换
1 let data1 = [ 2 {id:1,pid:0,name:"name1"}, 3 {id:2,pid:0,name:"name2"}, 4 {id:3,pid:2,name:"name3"}, 5 {id:4,pid:1,name:"name4"}, 6 {id:5,pid:4,name:"name5"}, 7 {id:6,pid:5,name:"name6"}, 8 {id:7,pid:5,name:"name6"}, 9 {id:8,pid:7,name:"name6"}, 10 11 ] 12 //递归法:一维数组转无限极树状结构 13 /** 14 * 15 * @param data 数据源,一维数据 16 * @param idKeys 要匹配所在项的唯一idkey 属性字段,比如idkeys ='id', 17 * @param pidKeys 要匹配所在项的上级 pidkey 属性字段,比如pidkeys = 'pid', 18 * @param pid 要匹配所在项目的上级pidkey 字段的值,比如 pid = 0 19 * @param leve 当前所在树状结构的层级数 20 */ 21 export function oneToTree<T extends {[key:string]:any}>(data:T[],idKeys?:string,pidKeys?:string,pid?:any,leve?:number){ 22 let idKey = idKeys||"id" 23 let pidKey = pidKeys||'pid' 24 let leveKey = "$leve" 25 let childrenKey = "children" 26 let pidData = pid||0 27 let leves = leve||1; 28 if(!Array.isArray(data)) return data; 29 type resI = T&{$leve:number,children:resI[]};//使用交叉类型,新增了两个字段$live,children 30 let resArr:Array<resI> =[]; 31 data.forEach( (itme:any)=> { 32 if (itme[pidKey] === pidData) { 33 itme[leveKey] = leves; 34 itme[childrenKey] = oneToTree(data, idKey, pidKey, itme[idKey], leves + 1); 35 resArr.push(itme) 36 } 37 }) 38 39 return resArr 40 41 } 42 let data1 = oneToTree(data1)
二、数状结构数据转一维数组
1 /** 2 * @param data 数据源(数状结构) 3 * @param childrenKeys : 每项的子级字段key,默认为:children 4 */ 5 export function treeToOneArr<T extends {[key:string]:any}>(data:T[],childrenKey?:string):T[]{ 6 let resArr:T[] = []; 7 childrenKey = childrenKey||'children' 8 for(let i=0;i<data.length;i++){ 9 let itme:any = data[i];// 这里有点不好,用了any 类型,返回数据的成员掉失了类型检测, 10 if(Array.isArray(itme[childrenKey])){ 11 let child:T[] = itme[childrenKey]; 12 itme[childrenKey] = []; 13 resArr = resArr.concat(itme).concat(treeToOneArr(child,childrenKey)) 14 }else{ 15 resArr.push(itme) 16 } 17 } 18 19 return resArr 20 } 21 22 console.log(treeToOneArr(data4));
三、 一维数组,找所有上级或者下级指定数据
(1. ) :每项之间依赖字段存在上下层关系
(2. ):给出指定字段的值找出当前项所有的下级/上级,匹配项的指定字段的值或者匹配的所有项
let data1 = [ {id:1,pid:0,name:"name1"}, {id:2,pid:0,name:"name2"}, {id:3,pid:2,name:"name3"}, {id:4,pid:1,name:"name4"}, {id:5,pid:4,name:"name5"}, {id:6,pid:5,name:"name6"}, {id:7,pid:5,name:"name6"}, {id:8,pid:7,name:"name6"}, ] /** * 一维数组,每项之间依赖字段存在上下层关系,根据依赖项字段,给出指定字段的值找出当前项所有的下级/上级指定字段/所有项 * @param data ,数据源,一维数组 * @param value 给出要与依赖字段(PidKeys) 匹配的值 * @param idKeys 所在项的唯一key ,也是作为下级的依赖字段,默认为id,比如:id,pid * @param pidKeys 要与指定value 匹配的字段(不是值,是字段key),也是所在项的依赖字段,默认为pid,比如,id,pid * @param reKeys 要返回的指定字段值,默认为 和idkeys一样的 * @param field 是否要返回匹配项的所有字段,默认为false */ /* 1. 找所有上级:把每项的存在依赖关系的字段(如pid)作为匹配字段(idkeys),依赖字段作为为匹配字段 2. 找所有下级:和上级刚好相反 */ export function findTreenField<T extends {[key:string]:any}>(data:T[],value:any,idKeys?:string,pidKeys?:string,reKeys?:string,field?:boolean){ let idKey = idKeys||"id" let pidKey = pidKeys||"pid" let reKey = reKeys||idKey; let fields = field||false if(!value ||value===0) return []; if(!Array.isArray(data)) return []; var resArr:any[] = []; for (let i = 0; i < data.length; i++) { let itme:T = data[i]; if(itme[pidKey]===value){ if(fields){ resArr.push(itme); }else{ resArr.push(itme[reKey]); } resArr = resArr.concat(findTreenField(data, itme[idKey],idKey, pidKey, reKey,fields)) } } return resArr } // 找所有子级 console.log(findTreenField(data1,5)) ;//[6, 7, 8] //找所有父级 console.log(findTreenField(data1,5,"pid","id")) ;//[4,1,0]
四、 树状结构数据,根据指定值找所有上级节点(只需要知道子节点的属性key)
1. 递归法
2. 思路: 先递归数组往下找,根据当前属性keys的值如果和value 相等,找到要匹配当前value 所在的项,退出当前循环, 把当前的项的属性kesy对应的值作为value 参数,递归循环,一层层往上找
1 const data = [ 2 {id:1,children:[ 3 {id:1.1,children:null}, 4 {id:1.2,children:null}, 5 {id:1.3,children:[ 6 {id:1.31,children:null}, 7 {id:1.32,children:[ 8 {id:1.321,children:[ 9 {id:1.3211,children:null} 10 ]}, 11 {id:1.322,children:null} 12 ]} 13 ]}, 14 ]}, 15 16 {id:2,children:[ 17 {id:2.1,children:[ 18 {id:2.11,children:null}, 19 {id:2.12,children:[ 20 {id:2.121,children:null} 21 ]}, 22 {id:2.13,children:null}, 23 ]}, 24 ]}, 25 ] 26 27 /** 28 * 29 * @param dataArr 数据源(数状结构tree) 30 * @param value 要匹配的值 31 * @param keys 与value 匹配的属性keys ,比如'id' ,'index' 对象的值 32 * @param rekeys 要返回的 属性 reskeys,比如'id' ,'index' 对应的值 33 * @param childrenKeys 子节点的属性,默认 children 34 */ 35 export function findTreeParendId<T extends {[key:string]:any}>(dataArr:T[],value:any,keys:string,rekeys:string,childrenKeys?:string):Array<keyof T>{ 36 let data = JSON.parse(JSON.stringify(dataArr));//避免引用,做深拷贝处理 37 var resArr:Array<keyof T> = []; 38 let childrenKey = childrenKeys||'children'; 39 if(data.length<0){ 40 return resArr 41 } 42 let recursion = (arrs:T[],itmeId:any,parendId?:any)=>{ 43 for(let i=0;i<arrs.length;i++){ 44 45 let itme:T = arrs[i] 46 if(itme[keys]===itmeId){ 47 resArr.unshift(itme[rekeys]);// 找到匹配的就加进去 48 if(parendId){ 49 recursion(data,parendId) 50 } 51 break;//跳出当前循环 52 }else{ 53 //找不到,如果有子级,递归往下找 54 if(itme[childrenKey]&& Array.isArray(itme[childrenKey])){ 55 recursion(itme[childrenKey],itmeId,itme[keys]) 56 } 57 } 58 } 59 } 60 recursion(data,value) 61 return resArr; 62 } 63 console.log(findTreeParendId(data,2.121,"id","id"));//[2, 2.1, 2.12, 2.121]
五、 树状结构数据,根据指定值找所有下级节点(只需要知道子节点的属性key)
1、使用递归法
2、实现思路和 第四个找所有父级节点是一样,但是实现有点不同(有更好的实现方法可以留言)
1 const data = [ 2 {id:1,children:[ 3 {id:1.1,children:null}, 4 {id:1.2,children:null}, 5 {id:1.3,children:[ 6 {id:1.31,children:null}, 7 {id:1.32,children:[ 8 {id:1.321,children:[ 9 {id:1.3211,children:null} 10 ]}, 11 {id:1.322,children:null} 12 ]} 13 ]}, 14 ]}, 15 16 {id:2,children:[ 17 {id:2.1,children:[ 18 {id:2.11,children:null}, 19 {id:2.12,children:[ 20 {id:2.121,children:null} 21 ]}, 22 {id:2.13,children:null}, 23 ]}, 24 ]}, 25 ] 26 27 /** 28 * 29 * @param data 数据源(数状结构tree) 30 * @param value 给出指定要匹配的值 比 1 31 * @param idkeys 被匹配的字段属性 ,比如:id(默认) 32 * @param reKeys 要返回的字段属性,比如 id(默认) 33 * @param childrenKeys 指定每项的子级字段,比如:children(默认) 34 */ 35 export function findChildFiled<T extends {[key:string]:any}>(data:T[],value:any,idkeys?:string,reKeys?:string,childrenKeys?:string){ 36 let idkey = idkeys||'id'; 37 let reKey = reKeys||'id'; 38 let childrenKey = childrenKeys||'children' 39 let arrRes:any[] = []; 40 //2.对匹配的所在项,进行递归获取所有子项的指定字段值 41 let findReKeys = function(arr:T[]){ 42 if(!Array.isArray(arr)) return arr; 43 for(let i =0;i<arr.length;i++){ 44 let itme:T = arr[i]; 45 arrRes.push(itme[reKey]) 46 findReKeys(itme[childrenKey]) 47 } 48 } 49 //1.先递归找到指定值的所在项 50 let findNode = function(arr:T[]){ 51 if(!Array.isArray(arr)) return arr; 52 for(let i =0;i<arr.length;i++){ 53 let itme:T = arr[i]; 54 if(itme[idkey]===value){ 55 findReKeys([itme]) 56 break; 57 }else{ 58 findNode(itme[childrenKey]) 59 } 60 61 } 62 } 63 findNode(data) 64 return arrRes 65 } 66 console.log(findChildFiled(data,1.3));//[1.3, 1.31, 1.32, 1.321, 1.3211, 1.322] 67 console.log(findChildFiled(data,2.1));//[2.1, 2.11, 2.12, 2.121, 2.13]