zoukankan      html  css  js  c++  java
  • 关于树状结构数据的一些常用处理,比如找所有父级和子级,一维数组转无限级树状结构

    树状结构数据在日常开发是最经常遇到的数据,比如一些后台管理系统左侧菜单就是一个树状结构的数据,这些数据的特点有,可以无限的子节点,父级与子级一般会存在上级关系,比如子级的属性会有父级的唯一标识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]
  • 相关阅读:
    C++输入与输出
    数组与指针
    MFC+WinPcap编写一个嗅探器之零(目录)
    netty源码分析之揭开reactor线程的面纱(二)
    netty源码分析之揭开reactor线程的面纱(一)
    Vert.x 线程模型揭秘
    理解 RxJava 的线程模型
    Java RESTful 框架的性能比较
    Java借助CountDownLatch完成异步回调
    在 Java 中运用动态挂载实现 Bug 的热修复
  • 原文地址:https://www.cnblogs.com/beyonds/p/13570531.html
Copyright © 2011-2022 走看看