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

Javascript 使用selection.filter获取多个选择的最佳方法,javascript,d3.js,Javascript,D3.js,我只是在玩d3.js,我想知道使用selection.filter()或其他方法在一个循环中获得多个选择的最佳方法是什么 从数组的角度考虑,我将使用array.prototype.filter()获取所需的数据集。每当我需要基于不同条件的多组数据时,我都会使用Array.prototype.reduce(),并将数据作为对象推送到累加器中的各个键 因此,对于D3选择,我将如何在单个循环中过滤并获得不同条件的选择(类似于D3.reduce())。这样我就可以在过滤后的选择上使用选择方法。我阅读了文

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

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


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

D3js有一个过滤功能用于选择

来自

选择。过滤器(过滤器)

过滤选择,返回一个新的选择,该选择仅包含指定过滤器为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); });

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

您的问题很有趣:如何在单个循环中过滤一个选择并填充多个选择。然而,我必须说,它不仅有趣,而且可能毫无用处:惯用的方法非常简单,只需要做几个过滤器:

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

我的第一个想法是使用
选择.merge
,但我不得不很快放弃它,因为正如Bostock(D3创建者)所说

selection.merge的当前实现仅处理两个选择具有相同结构(即相同的父项和索引)的情况,并返回具有相同结构的选择

因此,我决定只连接节点,这可以通过
Array.prototype.concat
实现。这就是想法:首先,我们声明一些空的选择

let foo = d3.selectAll(null);
let bar = d3.selectAll(null);
let baz = d3.selectAll(null);
然后,在较大的选择中使用
每个
,检查属性(此处命名为
标签
),并相应地连接节点:

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个圆圈,全部为黑色。然后,在
每个
中,我们填充三个选项(
circlesFoo
circlesBar
circlesBaz
),分别用绿色、红色和蓝色绘制:

const数据=[{
x:20,
标签:“foo”
},
{
x:50,
标签:“酒吧”
}, {
x:80,
标签:“foo”
}, {
x:110,
标签:“baz”
}, {
x:140,
标签:“酒吧”
}, {
x:170,
标签:“baz”
}, {
x:200,
标签:“baz”
}, {
x:230,
标签:“foo”
}, {
x:260,
标签:“foo”
}, {
x:290,
标签:“酒吧”
},
];
const svg=d3.选择(“svg”);
const circles=svg.selectAll(空)
.数据(数据)
.输入()
.附加(“圆圈”)
.attr(“cy”,75)
.attr(“r”,10)
.attr(“cx”,d=>d.x);
设circlesFoo=d3.selectAll(null);
设circlesBar=d3。选择全部(空);
设circlesBaz=d3.selectAll(null);
圆。每个(函数(d){
如果(d.标签==“foo”){
circlesFoo=d3.selectAll(circlesFoo.nodes().concat(此))
}否则如果(d.标签==“条”){
circlesBar=d3.selectAll(circlesBar.nodes().concat(此))
}否则{
circlesBaz=d3.selectAll(circlesBaz.nodes().concat(此))
}
});
圆形样式(“填充”、“绿色”);
圆形条样式(“填充”、“红色”);
圆形样式(“填充”、“蓝色”)


Null选择,这是我没有想到的。我不知道这是可能的。好的。让我试试this@DharanBro看看我写的关于
selectAll(null)
的这个Q/a对:正如你提到的惯用方法,你不认为没有必要为不同的条件反复运行过滤器吗?当bigSelection很大时,它会影响性能。为什么D3不想用一个内置的api来处理这个问题呢?有趣的是,这比惯用的方法慢50倍,因为在循环过程中创建的所有临时选择在下一次迭代中都会被丢弃:不过,这可以通过在过滤过程中先设置节点数组来避免,然后再选择(请参阅测试用例“.each()optimized”)。优化版本的执行速度是惯用解决方案的两倍。@DharanBro。这类问题的回答只是为了挑战,但应该添加“警告选择器,不要这样做…”。
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))
  }
});