遍历对象数组以生成 d3 桑基图数据
Posted
技术标签:
【中文标题】遍历对象数组以生成 d3 桑基图数据【英文标题】:Traverse Array of objects to generate d3 Sankey Chart data 【发布时间】:2019-06-30 21:43:41 【问题描述】:必须将此输入(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
);
我的解决方案只有在只有一个级别深的存储桶时才有效。如何为孩子内的多个嵌套桶实现这一点?
【问题讨论】:
【参考方案1】:我采用递归方法来生成预期的输出。 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));
【讨论】:
以上是关于遍历对象数组以生成 d3 桑基图数据的主要内容,如果未能解决你的问题,请参考以下文章
可视化应用实战案例:绘制交互式+pdf+png等多格式桑基图