简体   繁体   English

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

[英]Can I merge new and existing data in a D3 join?

I want to maintain a list of elements indexed by a key, that acts like an accumulator on that key. 我想维护一个由键索引的元素的列表,该列表的作用类似于该键上的累加器。

I have a real-time update cycle which repeatedly generates a subset of items indexed on the same key, and I want to animate the accumulation process as they are absorbed into the existing list. 我有一个实时更新周期,该周期会重复生成索引在同一键上的项的子集,并且我希望对累积过程进行动画处理,因为它们被吸收到现有列表中。

In my simple example, I just want to count the number of times I've seen each key. 在我的简单示例中,我只想计算我看到每个键的次数。

If I had all the data to start with, I'd just use d3.nest() , but I don't want to reaggregate the entire history or redraw every item every time I get an update. 如果我有所有的数据开始,我将只使用d3.nest() ,但是我不想在我每次更新时都重新汇总整个历史记录或重画每个项目。

I'd like to use D3 to join my new data items into the existing selection, and merge the old and new data for each key, but I can't see how to avoid my new data items just overwriting any existing data. 我想使用D3将新数据项加入到现有选择中,并合并每个键的旧数据和新数据,但是我看不到如何避免新数据项仅覆盖任何现有数据。 Is there a way to combine the existing datum with the new datum to produce a merged datum? 是否可以将现有基准与新基准合并以生成合并基准?

<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>

Above outputs: 以上输出:

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

My non-D3-ish way to do it is to pull out the matching data elements, merge, and then reinsert, like so: 我这样做的非D3方式是拔出匹配的数据元素,合并,然后重新插入,如下所示:

...

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 });

I ended up using a d3.map() to do a manual in-place merge via the filter function, which seems a bit more D3-ish. 我最终使用d3.map()通过过滤器功能进行了手动就地合并,这看起来有点像D3。 This doesn't deal with new items, but in my case I know the base grid ahead of time, I just don't know which points I want to update each time step. 这不会处理新项目,但就我而言,我提前知道了基本网格,只是不知道每个时间步要更新哪些点。

... 

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 });

...

I'm not sure of an idiomatic D3 way of handling this, but accomplishing what you wish should be as easy as augmenting your ticks data by comparing to the selection data before binding, ie: 我不确定是否有惯用的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;
});

Note that this also accounts for potential new elements 请注意,这也考虑了潜在的新元素

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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