[英]Why create arrays of properties in D3 Force-Directed Graph example?
我正在为一个项目改编 Mike Bostock 的D3 Force Directed Graph 示例。
我不明白他为什么从节点/链接创建单个属性的 arrays 然后稍后引用那些 arrays。
例如:
// this creates an array of node ids like ["id1", "id2", ...]
const N = d3.map(nodes, nodeId).map(intern);
// ...
nodes = d3.map(nodes, (_, i) => ({id: N[i]}));
// ...
const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
为什么不这样做:
nodes = d3.map(nodes, (d) => ({id: d.id}));
// ...
const forceLink = d3.forceLink(links).id((d) => d.id);
简单多了,最后的结果好像也一样。 但这是 D3 的创建者的官方示例,所以也许他这样做有我不理解的原因?
实际上他有一个例子,他没有在这里创建 arrays 。
我尝试记录变量值以理解代码,但我仍然不明白他为什么那样做。
这是最佳实践的问题。 N
不是单个属性的数组(您可能指的是“具有单个属性的对象数组”),它是一个基元数组。 此外,Bostock 正在使用 JavaScript 惯例,即常量全部大写,例如N
(节点)、 LS
(链接源)、 LT
(链接目标)等。
线索就在那之后出现,在他的评论中:
// 将输入节点和链接替换为模拟的可变对象。
因此, nodes
和links
是可变的 arrays,模拟对其进行操作(模拟本身为每个对象添加了几个属性),同时他使用原始的不可变值保留对 arrays 的引用。 最后,请注意对象是可变的,而字符串(原始类型)不是。
该示例构建了一个 function 的可配置组件ForceGraph
,它采用两个 arguments:1) 一个包含属性nodes
和links
的 object 加上 2) 一个包含用于自定义ForceGraph
组件的属性的 object:
function ForceGraph({ // <== first parameter, nodes and links
nodes, // an iterable of node objects (typically [{id}, …])
links // an iterable of link objects (typically [{source, target}, …])
}, { // <== second parameter, configuration
nodeId = d => d.id, // given d in nodes, returns a unique identifier (string)
/* more config options */
} = {}) {
// Compute values.
const N = d3.map(nodes, nodeId).map(intern);
const LS = d3.map(links, linkSource).map(intern);
const LT = d3.map(links, linkTarget).map(intern);
function ForceGraph
定义了一个可重用的组件,它可以以任意格式获取节点和链接的数据。 这就是为什么nodes
行上的评论说
一个可迭代的节点对象(通常是[{id}, …])
配置对象(即第二个参数)的第一个属性定义了一个访问器 function 用于提取节点的唯一标识符:
nodeId = d => d.id, // given d in nodes, returns a unique identifier (string)
这就是为什么有一个迭代数组N
包含使用所述访问器 function 计算的那些标识符的原因。数组N
(以及所有其他辅助对象和数组)用于将外部数据结构规范化为内部表示:
// Replace the input nodes and links with mutable objects for the simulation.
nodes = d3.map(nodes, (_, i) => ({id: N[i]}));
links = d3.map(links, (_, i) => ({source: LS[i], target: LT[i]}));
中间步骤提供了从外部数据到内部数据的抽象。 这确保了ForceGraph
的内部工作始终可以作用于相同的预定义数据结构,无论外部数据结构可能是什么样子。
例如,如果您的节点数据看起来像
data = {
nodes : [{
key: { main: "m", sub: "1" }
}, {
key: { main: "m", sub: "2" }
}, {
/* ...*/
}],
links: { /* ... */
};
您可以通过自定义访问器 function 调用ForceGraph
,该访问器返回节点的唯一化合物键:
graph = ForceGraph(data, {
nodeId: d => d.key.main + d.key.sub
})
由于输入数据的中间转换,这就是使图形工作所需的一切,而无需更改组件的任何内部工作方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.