常用树操作函数ts版

521 阅读3分钟

基础配置

// 默认配置
const DEFAULT_CONFIG = {
  id: 'id',
  children: 'children',
  pid: 'pid',
};

函数集合

  • 数组转树列表

    export function listToTree<T = any>(list: any[], config = DEFAULT_CONFIG): T[] {
      const nodeMap = new Map();
      const result: T[] = [];
      const { id, children, pid } = config;
    ​
      for (const node of list) {
        node[children] = node[children] || [];
        nodeMap.set(node[id], node);
      }
      for (const node of list) {
        const parent = nodeMap.get(node[pid]);
        (parent ? parent[children] : result).push(node);
      }
      return result;
    }
    

    let d =[{"id":1,"pid":0,"name":"1"},{"id":2,"pid":0,"name":"2"},{"id":3,"pid":0,"name":"3"},{"id":4,"pid":1,"name":"4"},{"id":5,"pid":4,"name":"5"},{"id":6,"pid":2,"name":"6"},{"id":7,"pid":3,"name":"7"},{"id":8,"pid":4,"name":"8"},{"id":9,"pid":8,"name":"9"}]

    转换结果为

    [{"id":1,"pid":0,"name":"1","children":[{"id":4,"pid":1,"name":"4","children":[{"id":5,"pid":4,"name":"5","children":[]},{"id":8,"pid":4,"name":"8","children":[{"id":9,"pid":8,"name":"9","children":[]}]}]}]},{"id":2,"pid":0,"name":"2","children":[{"id":6,"pid":2,"name":"6","children":[]}]},{"id":3,"pid":0,"name":"3","children":[{"id":7,"pid":3,"name":"7","children":[]}]}]

  • 树列表转数组

    export function treeToList<T = any>(tree: any, config= DEFAULT_CONFIG): T {
      const { children } = config;
      const result: any = [...tree];
      for (let i = 0; i < result.length; i++) {
        if (!result[i][children!]) continue;
        result.splice(i + 1, 0, ...result[i][children!]);
      }
      return result;
    }
    
  • 查找一个元素

    export function findNode<T = any>(
      tree: any,
      func: Fn,
      config=DEFAULT_CONFIG,
    ): T | null {
      const { children } = config;
      const list = [...tree];
      for (const node of list) {
        if (func(node)) return node;
        node[children!] && list.push(...node[children!]);
      }
      return null;
    }
    

    console.log(JSON.stringify(findNode(d, (o) => o.id > 6)))

    {"id":7,"pid":3,"name":"7","children":[]}

  • 查找所有符合条件的元素

    export function findNodeAll<T = any>(
      tree: any,
      func: Fn,
       config=DEFAULT_CONFIG,
    ): T[] {
      const { children } = config;
      const list = [...tree];
      const result: T[] = [];
      for (const node of list) {
        func(node) && result.push(node);
        node[children!] && list.push(...node[children!]);
      }
      return result;
    }
    
  • 查找符合条件的元素路径(祖先节点=>父节点=>查找节点)

    export function findPath<T = any>(
      tree: any,
      func: Fn,
      config=DEFAULT_CONFIG,
    ): T | T[] | null {
      const path: T[] = [];
      const list = [...tree];
      const visitedSet = new Set();
      const { children } = config;
      while (list.length) {
        const node = list[0];
        if (visitedSet.has(node)) {
          path.pop();
          list.shift();
        } else {
          visitedSet.add(node);
          node[children!] && list.unshift(...node[children!]);
          path.push(node);
          if (func(node)) {
            return path;
          }
        }
      }
      return null;
    }
    

    console.log(JSON.stringify(findPath(d, (o) => o.id == 5)))

    // 第一层
    [{"id":1,"pid":0,"name":"1","children":[{"id":4,"pid":1,"name":"4","children":[{"id":5,"pid":4,"name":"5","children":[]},{"id":8,"pid":4,"name":"8","children":[{"id":9,"pid":8,"name":"9","children":[]}]}]}]},
    // 第二层
     {"id":4,"pid":1,"name":"4","children":[{"id":5,"pid":4,"name":"5","children":[]},{"id":8,"pid":4,"name":"8","children":[{"id":9,"pid":8,"name":"9","children":[]}]}]},
     // 查找节点
    {"id":5,"pid":4,"name":"5","children":[]}]
    

    应用:如某用户属于行政部->人事科->材料管理员 ,可用该方法

  • 查找所有符合条件的元素路径

    export function findPathAll(tree: any, func: Fn,config=DEFAULT_CONFIG) {
      const path: any[] = [];
      const list = [...tree];
      const result: any[] = [];
      const visitedSet = new Set(),
        { children } = config;
      while (list.length) {
        const node = list[0];
        if (visitedSet.has(node)) {
          path.pop();
          list.shift();
        } else {
          visitedSet.add(node);
          node[children!] && list.unshift(...node[children!]);
          path.push(node);
          func(node) && result.push([...path]);
        }
      }
      return result;
    }
    

    console.log(JSON.stringify(findPath(d, (o) => o.id > 5)))

    数据结构[第一个,第二个,第三个],每一个数据同findPath()

  • 查找符合条件的元素(如果有父元素,则最后一级为查找元素)

    export function filterNode<T = any>(
      tree: T[],
      func: (n: T) => boolean,
      config=DEFAULT_CONFIG
    ): T[] {
      const children = config.children as string;
      function listFilter(list: T[]) {
        return list
          .map((node: any) => ({ ...node }))
          .filter((node) => {
            // 递归调用 对含有children项  进行再次调用自身函数 listFilter
            node[children] = node[children] && listFilter(node[children]);
            // 执行传入的回调 func 进行过滤
            return func(node) || (node[children] && node[children].length);
          });
      }
    ​
      return listFilter(tree);
    }
    

    console.log(JSON.stringify(filter(d, (o) => o.id == 4)))

    [  {   "id": 1,   "pid": 0,   "name": "1",   "children": [                {                "id": 4,                "pid": 1,                "name": "4",                "children": []
                    }
                  ]
      }
    ]
    
  • 递归处理树数据

    export function eachTree(treeDatas: any[], callBack: Fn, parentNode = {}) {
      treeDatas.forEach((element) => {
        const newNode = callBack(element, parentNode) || element;
        if (element.children) {
          eachTree(element.children, callBack, newNode);
        }
      });
    }
    
    eachTree(d, o) => {
      o.name = o.name + 'nihao'
      return o
    })
    console.log(JSON.stringify(d))
    //[{"id":1,"pid":0,"name":"1nihao","children":[{"id":4,"pid":1,"name":"4nihao","children":[{"id":5,"pid":4,"name":"5nihao","children":[]},{"id":8,"pid":4,"name":"8nihao","children":[{"id":9,"pid":8,"name":"9nihao","children":[]}]}]}]},{"id":2,"pid":0,"name":"2nihao","children":[{"id":6,"pid":2,"name":"6nihao","children":[]}]},{"id":3,"pid":0,"name":"3nihao","children":[{"id":7,"pid":3,"name":"7nihao","children":[]}]}]
    
  • 循环处理树数据

    export function forEach<T = any>(
      tree: T[],
      func: (n: T) => any,
      config=DEFAULT_CONFIG
    ): void {
      const list: any[] = [...tree];
      const { children } = config;
      for (let i = 0; i < list.length; i++) {
        //func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿
        if (func(list[i])) {
          return;
        }
        children && list[i][children] && list.splice(i + 1, 0, ...list[i][children]);
      }
    }
    

    forEach2(d, (o) => {

    o.name = o.name + 'nihao'

    // return true 就终止遍历

    return false

    })

完整代码 如果觉得文章对你有帮助,欢迎一键三连