JS树常用操作: 查找、遍历、筛选、树结构和列表结构转换

692 阅读2分钟

image.png

一、遍历树

1 树结构介绍

JS中树常见的结构为:

let tree = [
    {
        "id": 1,
        "lable": "一级 1",
        "children": [
            {
                "id": 2,
                "label": "二级 1-1",
                "children": [
                    {
                        "id": 4,
                        "label": "三级 1-1-1"
                    },
                    {
                        "id": 5,
                        "label": "三级 1-1-2"
                    }
                ]
            },
            {
                "id": 3,
                "label": "二级 1-2",
                "children": [
                    {
                        "id": 6,
                        "label": "三级 1-2-1"
                    },
                    {
                        "id": 7,
                        "label": "三级 1-2-2"
                    }
                ]
            }
        ]
    }
]

image.png

列子中的树是一个数组,树根列表数组组成,如果没有children属性或者children长度为0,则表示该节点为叶子节点

2 树的遍历方法介绍

image.png

3 广度优先遍历

  • 广度优先,循环实现

image.png

function filterTree(tree,func){
    let node = null; // 用空容器盛放
    let list = [...tree]; //将树拷贝一份
    // 每次从list中取出一个节点
    while(node = list.shift()){
        func(node);
        // 如果取出的节点有子节点,则将子节点平铺展开放入list
        node.children && list.push(...node.children); 
    }
}

4 深度优先遍历

image.png

function treeForeach (tree, func) {
  tree.forEach(data => {
    func(data)
    data.children && treeForeach(data.children, func) // 遍历子树
  })
}

5 深度优先循环实现

function treeForeach (tree, func) {
  let node, list = [...tree]
  while (node = list.shift()) {
    func(node)
    node.children && list.unshift(...node.children)
  }
}

二、列表和树相互转换

let lists = [
    {id:1,lable:"一级 1",parentId:''},
    {id:2,lable:"二级 1-1",parentId:1},
    {id:3,lable:"二级 1-2",,parentId:1},
    {id:4,lable:"三级 1-1-1",parentId:2},
    {id:5,lable:"三级 1-1-2",parentId:2},
    {id:6,lable:"一级 1",parentId:3},
    {id:7,lable:"一级 1",parentId:3},
]

1 列表转换成树

// 将列表转换成树,分为3步
1 遍历数组,找到根节点
2 维护一个的节点关系
3 将子节点放入对于的父节点中

function listToTree(list){
    let tree = []; //维护的节点关系
    let result = []; // 结果
    tree[m.id] = m;
    tree[m.id].children = [];
    list.forEach((m) =>{
        if(!m.parentId){
            result.push(m)
        } else {
           // 将子节点加入父节点中
            tree[m.parentId] &&  tree[m.parentId].children.push(m)
        }
    })
}

2 树转换成列表

  • 从遍历的方法里面随便选取一个将回调函数改为往数组里面放每一项既可
function treeToList(tree) {
    let result = [];
    let node = null;
    while(node = tree.shift()){
        result.push(node)
        node.children && tree.push(...node.children);
    }
	return result;
}

三、树结构筛选

  • 如找到某个节点所在的目录树
function treeFilter(tree,func){
    return tree.map((node) => ({...node}))
        .filter((node) => {
            node.children = node.children && treeFilter(node.children,func);
            return func(node) &&  node.children && node.children.length;
        })
}

四、树结构查找

查找节点其实就是一个遍历的过程,遍历到满足条件的节点则返回,遍历完成未找到则返回null。类似数组的find方法,传入一个函数用于判断节点是否符合条件,代码如下:

1 查找节点路径

function treeFindPath(tree, func, path = [], result = []) {
  for (let data of tree) {
    path.push(data.id);
    func(data) && result.push([...path]);
    data.children && treeFindPath(data.children, func, path, result);
    path.pop();
  }
  return result;
}