此输入(tree-like
结构)必须格式化为特定格式才能绘制d3 sankey diagram chart
。
let unformattedJson = [
{
"key": "a1",
"value": 30,
"buckets": [
{
"key": "a2",
"value": 10
},
{
"key": "b2",
"value": 20
}
]
},
{
"key": "b1",
"value": 70,
"buckets": [
{
"key": "b2",
"value": 40
},
{
"key": "c2",
"value": 30
}
]
}
]
我需要生成的预期输出是:
{
"nodes": [
{"nodeId":0,"name":"a1"},
{"nodeId":1,"name":"a2"},
{"nodeId":2,"name":"b2"},
{"nodeId":3,"name":"b1"},
{"nodeId":4,"name":"c2"}
],
"links": [
{"source":0,"target":1,"value":10},
{"source":0,"target":2,"value":20},
{"source":3,"target":2,"value":40},
{"source":3,"target":4,"value":30}
]
}
我的解决方案。我做了两个函数来计算节点和链接。对于节点,我做了一个递归函数以获取所有唯一键,并为每个键分配一个id
。然后,我做了另一个函数来获取键之间的所有关系。
let makeNodeObj = function(orObj, index){
let obj = {};
obj.nodeId = index;
obj.name = orObj;
return obj;
}
var getUniqueKeys = (old, arr)=>{
let toRet = old;
arr.forEach((data,index)=>{
if(toRet.indexOf(data.key)<0){ //remove duplicates
toRet.push(data.key);
}
if(data.buckets !== undefined && data.buckets.length>0){
getUniqueKeys(toRet, data.buckets);
}
});
return toRet;
}
let uniqueKeys = getUniqueKeys([],unformattedJson);
let nodes = uniqueKeys.map((data,index)=>{
return makeNodeObj(data,index);
});
let getNodeId = function(nodes, key){
let node = nodes.find((data)=>{
return data.name == key
});
return node.nodeId;
}
let links = [];
unformattedJson.map((data)=>{
let sourceId = getNodeId(nodes, data.key);
if(data.buckets.length>0){
data.buckets.map((data2)=>{
let targetId = getNodeId(nodes,data2.key);
let linkObj = {};
linkObj.source = sourceId;
linkObj.target = targetId;
linkObj.value = data2.value;
links.push(linkObj);
})
}
});
console.log({
nodes, links
});
我的解决方案仅在只有一个级别较深的存储桶时才有效。对于子级内部的多个嵌套存储桶,该如何实现?
答案 0 :(得分:4)
我采用了递归方法来生成预期的输出。 key
及其生成的id
之间的关联与Map保持一致。该方法使用Deep First Search算法遍历树的想法。
let unformattedJson = [
{
"key": "a1",
"value": 30,
"buckets": [
{
"key": "a2",
"value": 10,
"buckets": [
{"key": "a3", "value": 99}
]
},
{"key": "b2", "value": 20}
]
},
{
"key": "b1",
"value": 70,
"buckets": [
{"key": "b2", "value": 40},
{"key": "c2", "value": 30}
]
}
];
const getData = (input, visited=new Map(), parent, nodes=[], links=[]) =>
{
input.forEach(x =>
{
// Add node into the node list, if not visited previosuly.
if (!visited.has(x.key))
{
let currId = nodes.length;
nodes.push({nodeId: currId, name: x.key});
visited.set(x.key, currId);
}
// If a parent node exists, add relation into the links list.
if (parent)
{
// Note, we use the "Map" to get the ids.
links.push({
source: visited.get(parent.key),
target: visited.get(x.key),
value: x.value
});
}
// Traverse (if required) to the next level of deep.
if (x.buckets)
getData(x.buckets, visited, x, nodes, links)
});
return {nodes: nodes, links: links};
}
console.log(getData(unformattedJson));