如何将过滤器方法与更新模式一起使用?

时间:2018-09-19 20:30:24

标签: javascript d3.js

在下面的代码段中,我正在使用以下过滤器方法来获取所需的数据:

var data = csv.filter(d => d.date.includes(year))

这里的问题是,每当我更新图表时,每个柱形图都会从顶部重绘,而不是从先前的数据更新。

这是因为我在更新函数中重新声明了data变量,因此它没有从以前的数据中更新吗?

是否有一种简单的方法来解决此问题,还是我必须每年重新映射和分隔一个新的数据数组,并以此为基础建立我的更新模式?

代码如下:

var csvData = `date,value
2018-jan,100
2018-feb,75
2018-mar,45
2018-apr,65
2019-jan,21
2019-feb,43
2019-mar,55
2019-apr,33`;

var csv = d3.csvParse(csvData, d => d)

var margin = {top: 25, bottom: 25, left: 25, right: 25};

var svg = d3.select("#chart"),
	width = +svg.attr("width") - margin.left - margin.right, 
	height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand()
	.range([margin.left, width - margin.right])
	.padding(0.1)
	.paddingOuter(0.2)

var xAxis = g => g
	.attr("transform", "translate(0," + (height - margin.bottom) + ")")
	.call(d3.axisBottom(x).tickSizeOuter(0))

svg.append("g")
	.attr("class", "x-axis")

var y = d3.scaleLinear()
	.range([height - margin.bottom, margin.top])

update("2018")

function update(year) {

	var data = csv.filter(d => d.date.includes(year))

	y.domain([0, d3.max(data, d => Math.max(d.value))])

	x.domain(data.map(d => d.date))

	svg.selectAll(".x-axis")
		.call(xAxis);

	var bar = svg.selectAll(".bar")
		.data(data, d => d.date)
	
	bar.exit().remove();
	
	bar = bar
		.enter()
	.append("rect")
		.attr("class", "bar")
		.attr("fill", "steelblue")
		.attr("width", x.bandwidth())
		.attr("x", d => x(d.date))
		.merge(bar)
	.transition().duration(750)
		.attr("y", d => y(d.value))
		.attr("height", d => y(0) - y(d.value))
}

d3.select("#year").on("change", function() {
	update(this.value)
})
body {
	padding-top: 25px;
	margin: auto;
	width: 450px;
	font: 11px arial;
}	
<script src="https://d3js.org/d3.v5.min.js"></script>
Choose year
<select id="year">
	<option value="2018">2018</option>
	<option value="2019">2019</option>
</select><br>

<svg id="chart" width="450" height="200"></svg>

2 个答案:

答案 0 :(得分:2)

这里的问题与过滤数据,重新分配变量,合并选择或问题和注释中提到的任何其他问题无关。

这里的问题仅仅是key function。当您指定键功能时,每个元素的数据将根据其字符串标识符进行绑定。而且,由于日期不同,因此预期的行为是应删除小节并绘制新的小节。

您想要的不是索引连接,而是索引连接。因此,将数据方法从...更改为

.data(data, d => d.date)

.. to:

.data(data)

仅此更改是您的代码:

var csvData = `date,value
2018-jan,100
2018-feb,75
2018-mar,45
2018-apr,65
2019-jan,21
2019-feb,43
2019-mar,55
2019-apr,33`;

var csv = d3.csvParse(csvData, d => d)

var margin = {top: 25, bottom: 25, left: 25, right: 25};

var svg = d3.select("#chart"),
	width = +svg.attr("width") - margin.left - margin.right, 
	height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand()
	.range([margin.left, width - margin.right])
	.padding(0.1)
	.paddingOuter(0.2)

var xAxis = g => g
	.attr("transform", "translate(0," + (height - margin.bottom) + ")")
	.call(d3.axisBottom(x).tickSizeOuter(0))

svg.append("g")
	.attr("class", "x-axis")

var y = d3.scaleLinear()
	.range([height - margin.bottom, margin.top])

update("2018")

function update(year) {

	var data = csv.filter(d => d.date.includes(year))

	y.domain([0, d3.max(data, d => Math.max(d.value))])

	x.domain(data.map(d => d.date))

	svg.selectAll(".x-axis")
		.call(xAxis);

	var bar = svg.selectAll(".bar")
		.data(data)
	
	bar.exit().remove();
	
	bar = bar
		.enter()
	.append("rect")
		.attr("class", "bar")
		.attr("fill", "steelblue")
		.attr("width", x.bandwidth())
		.attr("x", d => x(d.date))
		.merge(bar)
	.transition().duration(750)
		.attr("y", d => y(d.value))
		.attr("height", d => y(0) - y(d.value))
}

d3.select("#year").on("change", function() {
	update(this.value)
})
body {
	padding-top: 25px;
	margin: auto;
	width: 450px;
	font: 11px arial;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
Choose year
<select id="year">
	<option value="2018">2018</option>
	<option value="2019">2019</option>
</select><br>

<svg id="chart" width="450" height="200"></svg>

答案 1 :(得分:1)

因此,我自己发现自己可以像这样在data方法本身中进行过滤...

    var bar = svg.selectAll(".bar")
        .data(data.filter(f => f.date.includes(year)))

...并且所有内容都已正确更新,不确定是否应该关闭问题,但还是将其保留为当前状态:

这是更新的代码:

var csvData = `date,value
2018-jan,100
2018-feb,75
2018-mar,45
2018-apr,65
2019-jan,21
2019-feb,43
2019-mar,55
2019-apr,33`;

var data = d3.csvParse(csvData, d => d)

var margin = {top: 25, bottom: 25, left: 25, right: 25};

var svg = d3.select("#chart"),
	width = +svg.attr("width") - margin.left - margin.right, 
	height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand()
	.range([margin.left, width - margin.right])
	.padding(0.1)
	.paddingOuter(0.2)

var xAxis = g => g
	.attr("transform", "translate(0," + (height - margin.bottom) + ")")
	.call(d3.axisBottom(x).tickSizeOuter(0))

svg.append("g")
	.attr("class", "x-axis")

var y = d3.scaleLinear()
	.range([height - margin.bottom, margin.top])

update("2018")

function update(year) {

	var maxVal = d3.max(data, function(d) {
		var f = d.date.includes(year)
		return (f == true ? d.value : null)
	})

	y.domain([0, maxVal]).nice()

	x.domain(data.map(d => d.date).filter(f => f.includes(year)))
	
	svg.selectAll(".x-axis")
		.call(xAxis);

	var bar = svg.selectAll(".bar")
		.data(data.filter(f => f.date.includes(year)));
    
	bar.exit().remove();
  
	bar.enter()
	.append("rect")
		.attr("class", "bar")
		.attr("fill", "steelblue")
		.attr("width", x.bandwidth())
		.attr("x", d => x(d.date))
		.merge(bar)
	.transition().duration(750)
		.attr("y", d => y(d.value))
		.attr("height", d => y(0) - y(d.value))
}

d3.select("#year").on("change", function() {
	update(this.value)
})
body {
	padding-top: 25px;
	margin: auto;
	width: 650px;
	font: 11px arial;
}
<script src="https://d3js.org/d3.v5.min.js"></script>	
Choose year
<select id="year">
	<option value="2018">2018</option>
	<option value="2019">2019</option>
</select><br>

<svg id="chart" width="650" height="200"></svg>