樹狀結構數據在日常開發是最經常遇到的數據,比如一些后台管理系統左側菜單就是一個樹狀結構的數據,這些數據的特點有,可以無限的子節點,父級與子級一般會存在上級關系,比如子級的屬性會有父級的唯一標識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]