我有一个图形编辑器,可以绘制 d3 力有向图(具有多项选择和结果的问卷,例如,如果您对问题 1 回答是,则转到问题 2,否则转到问题 1b)。< /p>
它有一个根节点,节点可以有多个父节点,多个子节点。
在内部,它表示为一个节点列表和一个链接列表。
const nodes = [
{id:1, data: {display: "start"}},
{id:2, data: {display: "question 1"}},
{id:3, data: {display: "question 2"}},
{id:4, data: {display: "question 1b"}},
{id:5, data: {display: "End"}},
];
const links = [
{source:1,target:2},
{source:2,target:3},
{source:2,target:4},
{source:4,target:3},
{source:3,target:5},
];
我需要生成一个显示所有可能路径的树,如下所示:
{
Id:1
Data: {display: "start"},
Children: [
{
Id:2,
Data: {display: "question 1"},
Children:[
{
Id:3,
Data: {display: "question 2"},
Children:[
{
Id:5
Data: "End",
Children:[]
}
]
},
{
Id:4,
Data: {display: "question 1 b"},
Children:[
{
Id:3,
Data: {display: "question 2"},
Children:[
{
Id:5
Data: "End",
Children:[]
}
]
}
]
}
]
}
]
}
到目前为止,这是我(非常幼稚)的尝试:
const nodes = [
{id:1, data: {display: "start"}},
{id:2, data: {display: "question 1"}},
{id:3, data: {display: "question 2"}},
{id:4, data: {display: "question 1b"}},
{id:5, data: {display: "End"}},
];
const links = [
{source:1,target:2},
{source:2,target:3},
{source:2,target:4},
{source:4,target:3},
{source:3,target:5},
]
const startNode = nodes.find(n => n.id == 1);
const jsonTree = {
id: startNode.id,
data: startNode.data,
children: [],
}
const nodeList = [];
nodeList.push(startNode);
while (nodeList.length > 0) {
const currentNode = nodeList.shift();
if (!currentNode) continue;
currentNode.visited = true;
const childNodes = this.findChildrenOfNode(currentNode, nodes, links);
const position = this.findPositionInJson(jsonTree, currentNode);
position.data = currentNode.data;
position.id = currentNode.id
childNodes.forEach((c) => {
if (!c.visited) {
c.visited = true;
nodeList.push(c);
}
const child = {
id:c.id,
data: c.data,
children: [],
};
position.children.push(child);
});
}
function findChildrenOfNode(node, nodes, links) {
const childLinksTargetIds = [];
links.forEach((l) => {
if (node.id === l.source) childLinksTargetIds.push(l.target);
});
return nodes.filter((n) => childLinksTargetIds.includes(n.id));
}
function findPositionInJson(jsonObject, node) {
if (!jsonObject) return null;
if (jsonObject.hasOwnProperty('data')) {
if (jsonObject.id === node.id) return jsonObject;
}
const keys = Object.keys(jsonObject);
for (let i = 0; i < keys.length; i += 1) {
const key = jsonObject[keys[i]];
if (typeof key === 'object') {
const o = this.findPositionInJson(key, node);
if (o != null) return o;
}
}
return null;
}
console.log(jsonTree)
我的代码将填充“1b”的直接孩子,但不会递归(“1b”的孩子是“2”,其孩子应该是“end”)
我想有一种算法可以准确地做我想做的事情。怎么办?
答案 0 :(得分:1)
首先创建一个映射,用于将 id
值映射到相应的对象。为每个对象添加一个空的 children
数组属性。然后从 children
信息填充 links
数组,每次在地图中查找 source
和 target
标识符。最后返回 id 1 的地图条目:
function buildTree(nodes, links) {
let map = new Map(nodes.map(o => [o.id, {...o, children: []}]));
for (let {source, target} of links) map.get(source).children.push(map.get(target));
return map.get(1);
}
// Demo
const nodes = [
{id:1, data: {display: "start"}},
{id:2, data: {display: "question 1"}},
{id:3, data: {display: "question 2"}},
{id:4, data: {display: "question 1b"}},
{id:5, data: {display: "End"}},
];
const links = [
{source:1,target:2},
{source:2,target:3},
{source:2,target:4},
{source:4,target:3},
{source:3,target:5},
]
console.log(buildTree(nodes, links));
答案 1 :(得分:1)
您可以使用递归来缩短解决方案:
const nodes = [
{id:1, data: {display: "start"}},
{id:2, data: {display: "question 1"}},
{id:3, data: {display: "question 2"}},
{id:4, data: {display: "question 1b"}},
{id:5, data: {display: "End"}},
];
const links = [
{source:1,target:2},
{source:2,target:3},
{source:2,target:4},
{source:4,target:3},
{source:3,target:5},
];
var nds = Object.fromEntries(nodes.map(x => [x.id, x.data]))
function to_tree(n = 1){
return {Id:n, data:nds[n], children:links.filter(x => x.source === n).map(({source:_, target:t}) => to_tree(t))}
}
console.log(to_tree())