我可以在D3连接中合并新数据和现有数据吗?

时间:2014-11-19 02:31:03

标签: javascript d3.js

我想维护一个由键索引的元素列表,它就像该键上的累加器一样。

我有一个实时更新周期,它会重复生成在同一个键上索引的项目子集,并且我希望在累积过程被吸收到现有列表中时为其设置动画。

在我的简单示例中,我只想计算我看过每个键的次数。

如果我开始使用所有数据,我只使用d3.nest(),但我不想重新整理整个历史记录或每次更新时重新绘制每个项目。

我想使用D3将我的新数据项加入现有选项,并合并每个键的新旧数据,但我无法看到如何避免我的新数据项覆盖任何现有数据。有没有办法将现有数据与新数据结合起来产生合并数据?

<html>
    <head>
        <script src="http://d3js.org/d3.v3.min.js"></script>
    </head
    <body>
        <div id="stuff">
        </div>

<script>

// set up a list of accumulators with count set to 0
d3.select('#stuff')
  .selectAll('.grid')
  .data(
    d3.range(10).map(function(i) { return {key: i*2, count: 0}; }),
    function(d) { return d.key; }
  )
 .enter().append('div')
  .classed('grid', true)
  .text(function(d) { return 'key: ' + d.key + ', count: ' + d.count });

// while(true) receive new items...

// now repeatedly receive a new list of items to accumulate into the existing list
var ticks = [{key: 4}, {key: 8}, {key: 14}];

// how can I combine the old and new datums during this join?
d3.selectAll('.grid')
    .data(
        ticks, 
        function(d) { return d.key; } 
        /*  Conceptually want to provide a function to merge old and new datum */
        //  , function(dnew, dold) { dold.count += 1; return dold; }
   )
  .text(function(d) { return 'key: ' + d.key + ', count: ' + d.count });

</script>

    </body>
</html>

以上输出:

key: 0, count: 0
key: 2, count: 0
key: 4, count: undefined  <== would like '1'
key: 6, count: 0
key: 8, count: undefined  <== would like '1'
key: 10, count: 0
key: 12, count: 0
key: 14, count: undefined  <== would like '1'
key: 16, count: 0
key: 18, count: 0

我的非D3-ish方法是拉出匹配的数据元素,合并然后重新插入,如下所示:

...

var tickmap = d3.map();
ticks.forEach(function(d){ tickmap.set(d.key, 1); });

var sel = d3.selectAll('.grid'),
    merged = sel.data()
        .filter(function(d) { return tickmap.has(d.key); })
        .map(function(d) { d.count += tickmap.get(d.key); return d; });

sel.data(merged, function(d) { return d.key; })
  .text(function(d) { return 'key: ' + d.key + ', count: ' + d.count });

2 个答案:

答案 0 :(得分:1)

我最终使用d3.map()通过过滤功能进行手动就地合并,这看起来有点像D3-ish。这不涉及新项目,但在我的情况下,我提前了解基本网格,我只是不知道每个时间步都要更新哪些点。

... 

var tickmap = d3.map();
ticks.forEach(function(d){ tickmap.set(d.key, 1); });

d3.selectAll('.grid')
  .filter(function(d) {  // filter and update only matching items
    if (tickmap.has(d.key)) {
      d.count += tickmap.get(d.key);
      return true;
    } else {
      return false;
    }
  })
  .style('color', 'red')  // redraw matching items in red
  .text(function(d) { return 'key: ' + d.key + ', count: ' + d.count });

...

答案 1 :(得分:0)

我不确定处理这种方法的惯用D3方法,但是通过与绑定前的选择数据进行比较来完成你想要的东西应该像增加你的刻度数据一样容易,即:

ticks.forEach(function(t) {
    var boundData = d3.selectAll('.grid').data().filter(function(d) { return d.key === t.key; });
    t.count = boundData[0] ? boundData[0].count += 1 : 0;
});

请注意,这也考虑了潜在的新元素

请参阅:http://jsfiddle.net/qAHC2/852/