繁体   English   中英

为什么在 D3 力导向图示例中创建 arrays 个属性?

[英]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 (链接目标)等。

线索就在那之后出现,在他的评论中:

// 将输入节点和链接替换为模拟的可变对象。

因此, nodeslinks是可变的 arrays,模拟对其进行操作(模拟本身为每个对象添加了几个属性),同时他使用原始的不可变值保留对 arrays 的引用。 最后,请注意对象是可变的,而字符串(原始类型)不是。

该示例构建了一个 function 的可配置组件ForceGraph ,它采用两个 arguments:1) 一个包含属性nodeslinks的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM