原数据格式
- 后端返回如下格式的数组,数组分为两个子数组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的形式
- 将转好的数组利用递归来获取路径名
- 路径数组的声明位置很重要,第二个数组完成后实际上是在操纵了他父级的变量