为什么d3更新整个数据 [英] Why d3 updates entire data

查看:106
本文介绍了为什么d3更新整个数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个svg元素,数据用这种方式创建:

  var chart = d3.select(#my-div ).append(svg); 
var chartData = [];
chartData.push([{x:1,y:3},{x:2,y:5}]);
chartData.push([{x:1,y:2},{x:2,y:3}]);

.domain([1,5]);
var lineFunc = d3.svg.line()
.x(function(d){
return xRange(dx);
})
.y (d){
return yRange(dy);
})
.interpolate('linear');

chart.append('g')。classed('lines',true).selectAll('path')。data(chartData).enter()
.append ')
.attr('d',function(d){
return lineFunc(d);
})
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill','none');

之后,我尝试更新我的数据并更新图表:

  chartData [1] .push({x:5,y:5}); 
chart.selectAll('g.lines')。selectAll('path')。data(chartData)
.attr('d',function(d){
console.log 'updating:');
console.log(d);
return lineFunc(d);
})
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill','none');

但是它打印两次'更新 ),但我只更改了一个( chartData [1] )。如何防止它不更新那些我没有改变?



//编辑到@ mef的回答

$ b。我将有许多功能,因此只有一个已更改时才会更新所有的功能。
$ b

我更改了数据(我不介意更新整个chartData [X]数据,我只想避免更新整个chartData):

  chartData.push({key:'A',data:[{x:1,y:3},{x:2,y:5}]}); 
chartData.push({key:'B',data:[{x:1,y:2},{x:2,y:3}]});

然后在添加数据时,我把 .data (d){return d.key}),当更新时我也是这样,但它仍然更新两个。



.data(chartData,function(d){return'A'}) .data '})更新数据时,它只更新一个,但始终使用 A 键(无论此函数返回 A B )。



因此,整个代码如下所示: / p>

  var chart = d3.select(#my-div)append(svg); 
var chartData = [];
chartData.push({key:'A',data:[{x:1,y:3},{x:2,y:5}]});
chartData.push({key:'B',data:[{x:1,y:2},{x:2,y:3}]});

var xRange = d3.scale.linear()。range([50,780])。domain([1,5]);
var yRange = d3.scale.linear()。range([380,20])。domain([2,9]);

var lineFunc = d3.svg.line()
.x(function(d){
return xRange(dx);
})
.y(function(d){
return yRange(dy);
})
.interpolate('linear');

chart.append('g')。classed('lines',true).selectAll('path')
.data(chartData,function(d){return d.key })。enter()
.append('path')
.attr('d',function(d){
return lineFunc(d.data);
} )
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill','none');

更新资料

 code> chartData [1] .data.push({x:5,y:5}); 
chart.selectAll('g.lines')。selectAll('path')
.data(chartData,function(d){return d.key})
.attr ',function(d){
console.log('updating:');
console.log(d);
return lineFunc(d.data);
}
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill','none');


解决方案

p>

选项1 - 使用键



这是一种懒惰的方法...



策略




  1. 设定可侦测数据变化的键功能。

    通过读取节点属性字符串并将其与对数据调用的属性生成器函数结果进行比较来实现此操作。

  2. 检测d3数据绑定过程的阶段或键盘数据),并为每个键使用不同的键:

    var k = Array.isArray(this)? lineD(d,lineFunc):d3.select(this).attr(d);


  3. 通过在数据密钥阶段期间从虚拟节点写入和读回两个密钥值。 (这是惰性部分!)


  4. 为更新,退出和输入选择保留单独的引用以分离其行为。



代码



  = d3.select(#my-div)。append(svg)
.attr(height,600)
.attr(width,900);
var chartData = [];
chartData.push([{x:1,y:3},{x:2,y:5}]);
chartData.push([{x:1,y:2},{x:2,y:3}]);

var xRange = d3.scale.linear()。range([50,780])。domain([1,5]);
var yRange = d3.scale.linear()。range([380,20])。domain([2,9]);

var lineFunc = d3.svg.line()
.x(function(d){
return xRange(dx);
})
.y(function(d){
return yRange(dy);
})
.interpolate('linear');

chart.append('g')。classed('lines',true).selectAll('path')
.data(chartData,key)
.enter ).append('path')
.attr('d',function(d){
return lineFunc(d);
})
.attr ,'black')
.attr('stroke-width',1)
.attr('fill','none');
//更新数据

chartData [1] .push({x:5,y:5});

var update = chart.selectAll('g.lines')。selectAll('path')
.data(chartData,key);
update.enter()。append('path')
.attr('d',function(d){
console.log('updating:');
console.log(d);
return lineFunc(d);
})
.attr('stroke','black')
.attr('stroke-width' ,1)
.attr('fill','none');
update.exit()。remove();

功能键(d,i,j){
var k = Array.isArray(this)? lineAttr(d,lineFunc,d):d3.select(this).attr(d);
console.log((Array.isArray(this)?data\t:node\t)+ k)
return k;

函数lineAttr(d,lineFunct,attribute){
var l = d3.select(svg)。selectAll(g)
.append ).style(display,none)
.attr(attribute,lineFunct(d))
d = l.attr
l.remove();
return d;
}
}

输出

 节点M50,328.57142857142856L232.5,225.71428571428572 
节点M50,380L232.5,328.57142857142856
数据M50,328.57142857142856L232.5,225.71428571428572
数据M50,380L232 .5,328.57142857142856L780,225.71428571428572

更新:
数组[3] 0:Object1:Object2:Objectlength:3__proto__:Array [0]
pre>

选项2 - 使用过滤器



这更有效,但仅适用于您知道只有数字



策略




  1. 加入没有键的数据,并通过将
    从绑定数据计算的属性字符串与DOM元素中的当前属性字符串进行比较来对其进行过滤。

  2. 与选项1中一样,使用虚节点作为延迟(和跨浏览器)方式来对齐节点属性和计算的属性文本的格式。



代码



  //更新资料

chartData [1] .push({x:5,y:5});

chart.selectAll('g.lines')。selectAll('path')
.data(chartData)
.filter(changed)
.attr 'd',function(d){
console.log('updating:');
console.log(d);
return lineFunc(d);
}
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill','none');

函数已更​​改(d){
var s = d3.select(this);
console.log(data\t+ lineAttr(s.datum(),lineFunc,d));
console.log(node\t+ s.attr(d)); console.log(\\\

return lineAttr(s.datum(),lineFunc,d)!= s.attr(d);

function lineAttr(d,lineFunct,attribute){
var l = d3.select(svg)。selectAll(g)
.append ).style(display,none)
.attr(attribute,lineFunct(d))
d = l.attr
l.remove();
return d;
}
}

输出

 资料M50,328.57142857142856L232.5,225.71428571428572 
节点M50,328.57142857142856L232.5,225.71428571428572

资料M50,380L232.5,328.57142857142856L780,225.71428571428572
节点M50,380L232.5,328.57142857142856

更新:
数组[3]


b $ b

选项3 - 两个世界的最佳



策略




  1. 使用标准更新/进入/退出模式。

  2. 在操作之前,过滤更新选择以形成更改选项。



代码



  //更新资料

alert 基础);
chartData [1] .push({x:5,y:5});
updateViz();
alert(change);
chartData.push([{x:3,y:1},{x:5,y:2}])
updateViz
alert(enter);
chartData.shift();
updateViz();
alert(exit);

function updateViz(){
var update = chart.selectAll('g.lines')。selectAll('path')
.data(chartData),

enter = update.enter()
.append('path')
.attr('d',function(d){
return lineFunc(d);
})
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill','none') ,

changed = update.filter(changed)
.attr('d',function(d){
console.log('updating:');
console.log(d);
return lineFunc(d);
});

update.exit()。remove();

函数已更​​改(d){
var s = d3.select(this);
console.log(data\t+ lineAttr(s.datum(),lineFunc,d));
console.log(node\t+ s.attr(d)); console.log(\\\

return lineAttr(s.datum(),lineFunc,d)!= s.attr(d);

function lineAttr(d,lineFunct,attribute){
var l = d3.select(svg)。selectAll(g)
.append ).style(display,none)
.attr(attribute,lineFunct(d))
d = l.attr
l.remove();
return d;
}
}
}



背景



阅读


I have an svg element with data created this way:

var chart = d3.select("#my-div").append("svg");
var chartData = [];
chartData.push([{x: 1, y: 3}, {x: 2, y: 5}]);
chartData.push([{x: 1, y: 2}, {x: 2, y: 3}]);

          .domain([1, 5]);
var lineFunc = d3.svg.line()
    .x(function (d) {
        return xRange(d.x);
    })
    .y(function (d) {
        return yRange(d.y);
    })
    .interpolate('linear');

chart.append('g').classed('lines', true).selectAll('path').data(chartData).enter()
    .append('path')
    .attr('d', function(d) {
        return lineFunc(d);
    })
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('fill', 'none');

After that I am trying to update my data and update the chart:

chartData[1].push({x: 5, y: 5});
chart.selectAll('g.lines').selectAll('path').data(chartData)
    .attr('d', function(d) {
        console.log('updating:');
        console.log(d);
        return lineFunc(d);
    })
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('fill', 'none');

but it prints 'updating' twice (for both of the chartData elements), but I've changed only one (chartData[1]). How to prevent it to not update the ones that I didn't change? I will have many functions, so it will be ineffiecient to update all of them when only one has changed.

// EDIT to @mef's answer

I changed data to (I don't mind updating entire chartData[X] data, I just want to avoid updating entire chartData):

chartData.push({key: 'A', data: [{x: 1, y: 3}, {x: 2, y: 5}]});
chartData.push({key: 'B', data: [{x: 1, y: 2}, {x: 2, y: 3}]});

and then when adding data I've put .data(chartData, function(d) {return d.key}) and when updating I did the same, but it still updates both.

I tried also to put .data(chartData, function(d) {return 'A'}) or .data(chartData, function(d) {return 'B'}) when updating the data and it updates only one, but always the data with A key (whether this function returns A or B).

So the whole code looks like this:

var chart = d3.select("#my-div").append("svg");
var chartData = [];
chartData.push({key: 'A', data: [{x: 1, y: 3}, {x: 2, y: 5}]});
chartData.push({key: 'B', data: [{x: 1, y: 2}, {x: 2, y: 3}]});

var xRange = d3.scale.linear().range([50, 780]).domain([1, 5]);
var yRange = d3.scale.linear().range([380, 20]).domain([2, 9]);

var lineFunc = d3.svg.line()
    .x(function (d) {
        return xRange(d.x);
    })
    .y(function (d) {
        return yRange(d.y);
    })
    .interpolate('linear');

chart.append('g').classed('lines', true).selectAll('path')
    .data(chartData, function(d) {return d.key}).enter()
    .append('path')
    .attr('d', function(d) {
        return lineFunc(d.data);
    })
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('fill', 'none');

updating data

chartData[1].data.push({x: 5, y: 5});
chart.selectAll('g.lines').selectAll('path')
    .data(chartData, function(d) {return d.key})
    .attr('d', function(d) {
        console.log('updating:');
        console.log(d);
        return lineFunc(d.data);
    })
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('fill', 'none');

解决方案

OK, it can be done...

Option 1 - use key

Here is a lazy way to do it...

Strategy

  1. Make a key function that will detect changes in data.
    Do this by reading the node attribute string and comparing it with the attribute generator function result, called on the datum.
  2. Detect the phase of the d3 data binding process (key on nodes or key on data) and use different key for each:
    var k = Array.isArray(this) ? lineD(d, lineFunc) : d3.select(this).attr("d");

  3. Align the formatting of the two key values by writing and reading back from a dummy node during the "data key" phase. (that's the lazy part!)

  4. Keep separate references for the update, exit and enter selections to decouple their behaviour.

Code

var chart = d3.select("#my-div").append("svg")
      .attr("height", 600)
      .attr("width", 900);
var chartData = [];
chartData.push([{x: 1, y: 3}, {x: 2, y: 5}]);
chartData.push([{x: 1, y: 2}, {x: 2, y: 3}]);

var xRange = d3.scale.linear().range([50, 780]).domain([1, 5]);
var yRange = d3.scale.linear().range([380, 20]).domain([2, 9]);

var lineFunc = d3.svg.line()
    .x(function (d) {
      return xRange(d.x);
    })
    .y(function (d) {
      return yRange(d.y);
    })
    .interpolate('linear');

chart.append('g').classed('lines', true).selectAll('path')
    .data(chartData, key)
    .enter().append('path')
    .attr('d', function(d) {
      return lineFunc(d);
    })
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('fill', 'none');
//updating data

chartData[1].push({x: 5, y: 5});

var update = chart.selectAll('g.lines').selectAll('path')
      .data(chartData, key);
update.enter().append('path')
      .attr('d', function (d) {
        console.log('updating:');
        console.log(d);
        return lineFunc(d);
      })
      .attr('stroke', 'black')
      .attr('stroke-width', 1)
      .attr('fill', 'none');
update.exit().remove();

function key(d, i, j) {
  var k = Array.isArray(this) ? lineAttr(d, lineFunc, "d") : d3.select(this).attr("d");
  console.log((Array.isArray(this) ? "data\t" : "node\t") + k)
  return k;

  function lineAttr(d, lineFunct, attribute) {
    var l = d3.select("svg").selectAll("g")
      .append("path").style("display", "none")
      .attr(attribute, lineFunct(d))
    d = l.attr(attribute);
    l.remove();
    return d;
  }
}

Output

node    M50,328.57142857142856L232.5,225.71428571428572          
node    M50,380L232.5,328.57142857142856                         
data    M50,328.57142857142856L232.5,225.71428571428572          
data    M50,380L232.5,328.57142857142856L780,225.71428571428572

updating:                                                      
Array[3]0: Object1: Object2: Objectlength: 3__proto__: Array[0]

Option 2 - use filter

This is more efficient but only applies if you know that only the number of points on the lines will change and the number of lines is fixed.

Strategy

  1. Join the data without a key function and filter it by comparing the attribute string calculated from the bound data, to the current attribute string in the DOM element.
  2. As in option 1, use a dummy node as a lazy (and cross-browser) way to align the formatting of the node attribute and the calculated attribute text.

Code

//updating data

chartData[1].push({x: 5, y: 5});

chart.selectAll('g.lines').selectAll('path')
  .data(chartData)
  .filter(changed)
  .attr('d', function (d) {
    console.log('updating:');
    console.log(d);
    return lineFunc(d);
  })
  .attr('stroke', 'black')
  .attr('stroke-width', 1)
  .attr('fill', 'none');

function changed(d) {
  var s = d3.select(this);
  console.log("data\t" + lineAttr(s.datum(), lineFunc, "d"));
  console.log("node\t" + s.attr("d")); console.log("\n")
  return lineAttr(s.datum(), lineFunc, "d") != s.attr("d");

  function lineAttr(d, lineFunct, attribute) {
    var l = d3.select("svg").selectAll("g")
      .append("path").style("display", "none")
      .attr(attribute, lineFunct(d))
    d = l.attr(attribute);
    l.remove();
    return d;
  }
}

Output

data    M50,328.57142857142856L232.5,225.71428571428572
node    M50,328.57142857142856L232.5,225.71428571428572

data    M50,380L232.5,328.57142857142856L780,225.71428571428572
node    M50,380L232.5,328.57142857142856

updating:
Array[3]

Option 3 - best of both worlds

Strategy

  1. Use a standard update/enter/exit pattern.
  2. Filter the update selection to form a "changed" selection before operating on it.

Code

//updating data

alert("base");
chartData[1].push({ x: 5, y: 5 });
updateViz();
alert("change");
chartData.push([{x: 3, y: 1}, {x: 5, y: 2}])
updateViz();
alert("enter");
chartData.shift();
updateViz();
alert("exit");

function updateViz() {
  var update = chart.selectAll('g.lines').selectAll('path')
      .data(chartData),

      enter = update.enter()
        .append('path')
        .attr('d', function (d) {
          return lineFunc(d);
        })
        .attr('stroke', 'black')
        .attr('stroke-width', 1)
        .attr('fill', 'none'),

      changed = update.filter(changed)
        .attr('d', function (d) {
          console.log('updating:');
          console.log(d);
          return lineFunc(d);
        });

  update.exit().remove();

  function changed(d) {
    var s = d3.select(this);
    console.log("data\t" + lineAttr(s.datum(), lineFunc, "d"));
    console.log("node\t" + s.attr("d")); console.log("\n")
    return lineAttr(s.datum(), lineFunc, "d") != s.attr("d");

    function lineAttr(d, lineFunct, attribute) {
      var l = d3.select("svg").selectAll("g")
        .append("path").style("display", "none")
        .attr(attribute, lineFunct(d))
      d = l.attr(attribute);
      l.remove();
      return d;
    }
  }
}

Background

Read this

这篇关于为什么d3更新整个数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆