d3js在编码解码数据之后工作

时间:2014-11-18 08:56:51

标签: javascript json d3.js

我正在使用d3js和hierarchical layout开展数据可视化工作。我的数据如下:

       0
     / | \
    /  |  \ 
   1   5   3
    \  |   |
     \ |   |
       4  /
       | /
       2

由于我无法链接到多个父级,因此我复制了显示的节点:

       0
     / | \
    /  |  \ 
   1   5   3
   |   |   |
   |   |   |
   4   4   |
   |   |   |
   2   2   2

我已经fiddle demo显示了我的问题:

  • 当我在JSON输入中使用正确的数据时,我有良好的布局(蓝色边框图形)。
  • 当我使用循环来解析我的JSON输入时,我有一个奇怪的图形(绿色边框图形)。

这是我用来解析输入的循环:

for (i in root[2].Arcs){
  var d = root[1].Nodes[root[2].Arcs[i].D];
  var s = root[1].Nodes[root[2].Arcs[i].S];
  if (!d.children){
    d.children = [];
  }
  d.children.push(s);
}

对我来说:控制台中的两个打印元素都是相同的,但不是布局的渲染。对象引用中可能存在一些不同之处。

我发现一个糟糕的解决方案是解码然后编码我的var:

    var root = JSON.parse(JSON.stringify(root));

然后脚本运行良好。但是我的root是一个很长的数组,解析需要很长时间......

有什么想法解释为什么我需要使用编码/解码来显示相同​​的东西吗?

谢谢

4 个答案:

答案 0 :(得分:1)

您应该编码/解码JSON以防止浅拷贝。要了解更多关于deepcopy和shallowcopy的信息,请点击链接。 What is the difference between a deep copy and a shallow copy?

作为     var root = JSON.parse(JSON.stringify(root)); 防止浅拷贝是一种错误的方法,你可以使用jquery的克隆方法或简单的javascripts切片方法来深度复制javascript数组。

例如,

var d=[1,2,3,4,5,6];//create array
var b=d;//copy array into another array (shallow copy/reference copy)
var e=d.slice();//clone array in another variable (deep copy)
d[0]=9; //change array element
console.log(d)// result : [9,2,3,4,5,6] (changed array)
console.log(b)// result : [9,2,3,4,5,6] (changed array due to reference)
console.log(e)// result : [1,2,3,4,5,6] (unchanged array due to deep copy)

另一种解决方案是使用下划线。如果您不想要完整的下划线javascript代码,那么您可以在下划线库中选择克隆部分。

在下划线中,您可以使用以下方法克隆对象数组:

var a = [{f: 1}, {f:5}, {f:10}];
var b = _.map(a, _.clone);       // <----
b[1].f = 55;
console.log(JSON.stringify(a));

会打印

[{"f":1},{"f":5},{"f":10}]

答案 1 :(得分:1)

你可以用这样的东西替换你的for循环,但是不要知道性能。 jsfiddle

traverse(root[1].Nodes[0]);
function traverse(node) {
    for (i in root[2].Arcs) {
        var d = root[1].Nodes[root[2].Arcs[i].D];
        if (node.name === root[2].Arcs[i].D) {
            var s = root[1].Nodes[root[2].Arcs[i].S];
            var sCopy={
                "name": s.name
            }
            traverse(sCopy);
            if (!node.children) {
               node.children = [];
            }
            node.children.push(sCopy);
        }
    }
}

答案 2 :(得分:1)

注意: 表现不如原帖,请参阅下面的评论。这种方式是可扩展的,所以无论对象(和标志看起来像什么),它都应该工作

<强>答案

这是&#34;深拷贝&#34;根据@transcranial和@LaxmikantDange的建议。

我建议使用jquery(我的首选方法:少写,做更多)并使用其extend例程:

<<load jquery>>

root = $.extend(true, {}, root[1].Nodes[0]);
graph(root2,svg);
graph(root,svg2);

请确保您true作为第一个参数,如http://api.jquery.com/jquery.extend/或此处What is the most efficient way to deep clone an object in JavaScript?(最佳答案)

不确定性能,但我希望它不是太糟糕。如果你测试,请告诉我!

请注意,根据您的申请(断开的链接等),深层副本可能会有问题。例如,我最近构建了一个在图表之间共享事件的反应应用程序 - 并且共享例如缩放事件(调整渲染对象)不会自动共享&#34; (因为对象是分开的,如果你知道我的意思。

答案 3 :(得分:0)

当您将其他节点(对象)推送到子节点数组上时,必须克隆这些对象(深度复制)。重新分配&#34; =&#34;仅适用于字符串或数字数组。在将子节点推送到阵列之前深度复制子节点会修复布局和渲染。

将子节点推送到数组中的原始代码:

for (i in root[2].Arcs){
  var d = root[1].Nodes[root[2].Arcs[i].D];
  var s = root[1].Nodes[root[2].Arcs[i].S];
  if (!d.children){
    d.children = [];
  }
  d.children.push(s);
}

修改:

for (i in root[2].Arcs){
  var d = root[1].Nodes[root[2].Arcs[i].D];
  var s = root[1].Nodes[root[2].Arcs[i].S];
  if (!d.children){
    d.children = [];
  }
  d.children.push(JSON.parse(JSON.stringify(s)));
}

检查出来:JSFiddle

此外,包括群集在内的d3层次布局支持多个父项开箱即用。为此,您可以尝试使用强制图布局。