我只是在玩d3.js,我想知道什么是使用selection.filter()
或以其他方式在单个循环中获得多个选择的最佳方法。
从数组的角度考虑,我将使用Array.prototype.filter()
来获取所需的数据集。每当我需要基于不同条件的多组数据时,我都会使用Array.prototype.reduce()
并将数据作为对象推送到累加器中的各个键。
因此,对于D3选择,我将如何在单个循环中过滤并获得针对不同条件的选择(类似于d3.reduce()
)。这样我就可以对过滤的选择使用选择方法。我阅读了文档,并且知道从v4开始,选择不再是数组。
答案 0 :(得分:2)
您的问题很有趣:如何在单个循环中过滤选择并填充多个选择。但是,我必须说,它就像它一样有趣,很可能是无用的:惯用的方法很简单,在于只需执行几个过滤器:
const bigSelection = d3.selectAll(foo);
const smallSelection1 = bigSelection.filter(function with condition 1);
const smallSelection2 = bigSelection.filter(function with condition 2);
const smallSelection3 = bigSelection.filter(function with condition 3);
//etc...
但是,只是出于好奇:这可能吗?是的。但是使用selection.each
,而不是selection.filter
。
我的第一个想法是使用selection.merge
,但是我不得不迅速放弃它,因为正如Bostock(D3创建者)所说,
selection.merge的当前实现仅处理两个选择具有相同结构(即,相同的父代和索引)并返回具有相同结构的选择的情况。
因此,我决定只连接节点,您可以使用Array.prototype.concat
进行连接。这就是想法:首先,我们声明一些空选择...
let foo = d3.selectAll(null);
let bar = d3.selectAll(null);
let baz = d3.selectAll(null);
然后,在更大的选择中使用each
,我们检查一个属性(在这里名为label
)并相应地连接节点:
bigSelection.each(function(d) {
if (d.label === "foo") {
foo = d3.selectAll(foo.nodes().concat(this))
} else if (d.label === "bar") {
bar = d3.selectAll(bar.nodes().concat(this))
} else {
baz = d3.selectAll(baz.nodes().concat(this))
}
});
这里是一个演示。大选择包含10个圆圈,所有圆圈均为黑色。然后,在each
中,我们填充三个选择(circlesFoo
,circlesBar
和circlesBaz
),分别用绿色,红色和蓝色绘制:
const data = [{
x: 20,
label: "foo"
},
{
x: 50,
label: "bar"
}, {
x: 80,
label: "foo"
}, {
x: 110,
label: "baz"
}, {
x: 140,
label: "bar"
}, {
x: 170,
label: "baz"
}, {
x: 200,
label: "baz"
}, {
x: 230,
label: "foo"
}, {
x: 260,
label: "foo"
}, {
x: 290,
label: "bar"
},
];
const svg = d3.select("svg");
const circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cy", 75)
.attr("r", 10)
.attr("cx", d => d.x);
let circlesFoo = d3.selectAll(null);
let circlesBar = d3.selectAll(null);
let circlesBaz = d3.selectAll(null);
circles.each(function(d) {
if (d.label === "foo") {
circlesFoo = d3.selectAll(circlesFoo.nodes().concat(this))
} else if (d.label === "bar") {
circlesBar = d3.selectAll(circlesBar.nodes().concat(this))
} else {
circlesBaz = d3.selectAll(circlesBaz.nodes().concat(this))
}
});
circlesFoo.style("fill", "green");
circlesBar.style("fill", "red");
circlesBaz.style("fill", "blue");
<svg></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
答案 1 :(得分:0)
D3js的选择具有过滤功能。
来自API
selection.filter(filter)<>
过滤选择,返回一个新选择,该选择仅包含指定过滤器为true的元素。过滤器可以指定为选择器字符串或函数。如果过滤器是一个函数,则会为每个选择的元素按顺序评估,依次传递当前数据(d),当前索引(i)和当前组(节点),并将其作为当前DOM元素(节点[i])。
这意味着如果您要根据数据附加一堆形状(例如圆形),则可以使用filter函数指定一个函数,如果该函数返回true,则将为那一点。
例如,
svg.selectAll("dot")
.data(data)
.enter()
.append("circle")
.filter(function(d) { return d.value < 100 }) //filters those data points below value of 100
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); });
如果您想编辑已经存在的对象,只需在选择后使用filter
函数,即selectAll
:
d3.selectAll("circle")
.filter(function(d) { return d.value < 20 }) //filters those data points below value of 20
.attr("fill", "blue");
很遗憾,您没有提及任何代码或特定示例来更好地指导您。