首页 > 解决方案 > 使用 selection.filter 获得多项选择的最佳方式

问题描述

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

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

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

标签: javascriptd3.js

解决方案


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

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在更大的选择中使用 an ,我们检查一个属性(这里命名为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>


推荐阅读