使用 selection.filter 获取多个选择的最佳方法



我只是在玩d3.js我想知道使用selection.filter()或其他方法在单个循环中获取多个选择的最佳方法是什么。

从数组的角度思考,我会使用Array.prototype.filter()来获取所需的数据集。每当我需要基于不同条件的多组数据时,我都会使用Array.prototype.reduce()并将数据作为对象推送到累加器中的相应键。

因此,使用 D3 选择,我将如何在单个循环中过滤并获得不同条件的选择。(类似于d3.reduce())。这样我就可以在过滤的选择上使用选择方法。我阅读了文档,我知道从 v4 开始,选择不再是数组。

您的问题很有趣:如何在单个循环中过滤选择并填充多个选择。但是,我必须说它和它一样有趣,可能没用:惯用的方法非常简单,只需执行几个过滤器:

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中,我们填充三个选择(circlesFoocirclesBarcirclesBaz),我们分别用绿色,红色和蓝色绘制:

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>

D3js 有一个过滤函数来选择。

从 API

选择.过滤器(过滤器) <>

筛选所选内容,返回仅包含指定筛选器为 true 的元素的新选择。筛选器可以指定为选择器字符串或函数。如果过滤器是一个函数,则针对每个选定元素对其进行计算,按顺序传递当前基准 (d)、当前索引 (i) 和当前组(节点),并将其作为当前 DOM 元素(节点[i])。

这意味着假设您有一堆形状(例如圆形),您打算根据数据附加这些形状(例如圆形),您可以利用过滤器函数指定一个函数,如果它返回 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");

不幸的是,您没有提到任何代码或特定示例来更好地指导您。

最新更新