利用递归将数组变成路径格式

461 阅读5分钟

原数据格式

  • 后端返回如下格式的数组,数组分为两个子数组node和children,node存着当前节点的信息(如节点名称,知识点等),children存着下一个节点的信息,children数组的结构也是有两个子数组node和children,具体结构如下图:

需求(附原数组)

  • 以下是原数组,尝试将数组数据处理并展示成本文展示效果的效果
// 将datalist的数据转换成以下格式:
//[
//   { sxName: "1", _path: ["1", "4", "5"], answer: "1" },
//   { sxName: "2", _path: ["2", "4", "5"], answer: "2" },
//   { sxName: "3", _path: ["3", "4", "5"], answer: "3" },
//];
let datalist = [{
            sxName: null,
            node: {
                id: "root",
                name: "root",
                hasChild: 1,
                hasContent: null,
                answer: null
            },
            children: [
                {
                    sxName: null,
                    node: {
                        id: "c11938cf2c5b4f5d93b48eb3b3974d80",
                        name: "这是子节点1",
                        hasChild: 1,
                        hasContent: 0,
                        answer: ""
                    },
                    children: [{
                        sxName: null,
                        node: {
                            id: "05c94a393a81401588e871b98c63d4a9",
                            name: "字节的4",
                            hasChild: 1,
                            hasContent: 0,
                            answer: null
                        },
                        children: [{
                            sxName: null,
                            node: {
                                id: "f83d3bcb58a2404e818d0038455738ae",
                                name: "44444",
                                hasChild: 0,
                                hasContent: 0,
                                answer: null
                            },
                            children: []
                        }]
                    }, {
                        sxName: null,
                        node: {
                            id: "75a0c3d5b1674de9a7e5c824057e50db",
                            name: "2222",
                            hasChild: 0,
                            hasContent: 1,
                            answer: "哈哈哈"
                        },
                        children: []
                    }, {
                        sxName: "专家优先入库条件",
                        node: {
                            id: "a9dacb802d1242e0a2a669637e24bc4f",
                            name: "这是子节点2",
                            hasChild: 0,
                            hasContent: 1,
                            answer: "呜呜呜呜呜"
                        },
                        children: []
                    }, {
                        sxName: "未满12周岁驾驶自行车、三轮车的",
                        node: {
                            id: "ebabe24700194c8bb01d3cf03ac6f770",
                            name: "子节点第二个",
                            hasChild: 0,
                            hasContent: 1,
                            answer: null
                        },
                        children: []
                    }]
                }]
        }]
  • 现在需要将数据以路径形式展示处理,路径最后一节是知识点(字段answer),其余为node的节点名称(字段name)

展示效果

源代码和解释

  • 源代码如下:(详细解释在代码的注释中)
 <ul id="list">
        <li class="lione"></li>

        <li class="litwo"></li>

        <li class="lithree"></li>
    </ul>
    <script>
        var ul = document.querySelector("#list")
        var one = document.querySelector(".lione")
        var two = document.querySelector(".litwo")
        var three = document.querySelector(".lithree")
        // sceneNodeList 的最终格式应为这样子 [
        //   { sxName: "1", _path: ["1", "4", "5"], answer: "1" },
        //   { sxName: "2", _path: ["2", "4", "5"], answer: "2" },
        //   { sxName: "3", _path: ["3", "4", "5"], answer: "3" },
        // ];
        let datalist = [{
            sxName: null,
            node: {
                id: "root",
                name: "root",
                hasChild: 1,
                hasContent: null,
                answer: null
            },
            children: [
                {
                    sxName: null,
                    node: {
                        id: "c11938cf2c5b4f5d93b48eb3b3974d80",
                        name: "这是子节点1",
                        hasChild: 1,
                        hasContent: 0,
                        answer: ""
                    },
                    children: [{
                        sxName: null,
                        node: {
                            id: "05c94a393a81401588e871b98c63d4a9",
                            name: "字节的4",
                            hasChild: 1,
                            hasContent: 0,
                            answer: null
                        },
                        children: [{
                            sxName: null,
                            node: {
                                id: "f83d3bcb58a2404e818d0038455738ae",
                                name: "44444",
                                hasChild: 0,
                                hasContent: 0,
                                answer: null
                            },
                            children: []
                        }]
                    }, {
                        sxName: null,
                        node: {
                            id: "75a0c3d5b1674de9a7e5c824057e50db",
                            name: "2222",
                            hasChild: 0,
                            hasContent: 1,
                            answer: "哈哈哈"
                        },
                        children: []
                    }, {
                        sxName: "专家优先入库条件",
                        node: {
                            id: "a9dacb802d1242e0a2a669637e24bc4f",
                            name: "这是子节点2",
                            hasChild: 0,
                            hasContent: 1,
                            answer: "呜呜呜呜呜"
                        },
                        children: []
                    }, {
                        sxName: "未满12周岁驾驶自行车、三轮车的",
                        node: {
                            id: "ebabe24700194c8bb01d3cf03ac6f770",
                            name: "子节点第二个",
                            hasChild: 0,
                            hasContent: 1,
                            answer: null
                        },
                        children: []
                    }]
                }]
        }]
        let sceneNodeList = []
        handleSceneNodeTree(datalist)//将原数组使用工具函数格式化,首次没有parent

        //两个工具函数-------------------------------------------------
        function handleSceneNodeTree(data, parent) {
            if (Array.isArray(data)) {
                data.forEach(item => {
                    let newObj = { ...item.node };//将节点信息用newObj存起来
                    parent && (newObj._parent = parent);//如果有parent,则表示是递归回来的newObj,以_parent存起来
                    if (newObj.hasContent) {
                        newObj._path = [];
                        newObj.sxName = item.sxName;//拿到知识点的名称
                        //存在知识点hasContent,则可以开始收集_path了
                        //此时_parent已经是多级结构,parent是node节点以多维数组的方式拼合
                        handleRouteStr(newObj, newObj._path);//传入空数组_path是因为需要将数据返回到这个函数中
                        //handleRouteStr函数会万财newObj._path部分,这就是为什么要在这里声明空数组的原因
                        sceneNodeList.push(newObj);
                    } else {
                        //不存在hasContent,证明没有知识点,还没走到尽头,则递归此函数,
                        //将原节点信息node以parent的形式递归
                        handleSceneNodeTree(item.children, newObj);
                        
                    }
                });
            }
        }
        function handleRouteStr(data, arr) {
            //传入一个以最低节点名称,有_parent参数,_parent参数还有_parent参数的数组,
            //和一个已经声明好的空数组newObj._path
            let pathName = data.id === "root" ? "根节点" : data.name;
            if (Array.isArray(arr) && pathName) {
                arr.unshift(pathName);//将节点名称name推入路径数组_path
            	//如果存在_parent,则重复这个函数,会将_parent里面的name再推入路径数组_path
                data._parent && handleRouteStr(data._parent, arr);
            }
        }
        //-------------------------------------------------------------------
        console.log("datalise=", datalist)
        console.log("sceneNodeList=", sceneNodeList)
        one.innerText = `1.知识点:${sceneNodeList[0].answer} \n-->路径:${sceneNodeList[0]._path.join("/")}`
        two.innerText = `2.知识点:${sceneNodeList[1].answer} \n-->路径:${sceneNodeList[1]._path.join("/")}`
        three.innerText = `3.知识点:${sceneNodeList[2].sxName} \n-->路径:${sceneNodeList[2]._path.join("/")}`
    </script>
  • 如上面的代码解释到,将数组中的node先存起来,如果发现node中不存在知识点hasContent,则将存起来的以第二个参数parent的形式传回去,传回去则变成下一级数组的parent参数,也就是说,从最外层开始,直到找到hasContent,最外层的node会一直以parent的形式赋值给里面的一层,变成如下图所示

  • 第二个函数handleRouteStr则是需要根据处理好的数组来获取路径,即有_parent就递归,直到递归到第一层的位置

注意点:

  • 先利用递归将数组转成指定的多维数组,parent的形式
  • 将转好的数组利用递归来获取路径名
  • 路径数组的声明位置很重要,第二个数组完成后实际上是在操纵了他父级的变量