【问题标题】:D3.js looping through an array of objectsD3.js 循环遍历对象数组
【发布时间】:2019-08-08 21:59:00
【问题描述】:

我正在尝试根据标题制作一排高度和宽度相等的条。有一个由 100 个对象组成的数组,每个对象都包含一个名为 resultskey 和其他 20 个对象的数组,因此最终结果必须是一排 2000 条,但我只得到 20 条:

Codepen:https://codepen.io/ekilja01/pen/RdJqGO

Data 是一个对象数组,其中包含一个名为 results 的对象数组,格式如下:

0:
page: 1
results: Array(20)
0: {vote_count: 17968, id: 19995, video: false, vote_average: 7.4, title: "Avatar", …}
...
length: 20

1: {page: 2, total_results: 406130, total_pages: 20307, results: Array(20)}
2: {page: 3, total_results: 406130, total_pages: 20307, results: Array(20)}

这是我的方法:

d3.json('data.json').then(data => {

  console.log(data);
  for (let i = 0; i < data.length; i++) {


    //x and y domain
    xScale.domain(data[i].results.map(d => d.title));
    yScale.domain(data[i].results.map(d => d.original_title));

    svg.selectAll('rect')
      .data(data[i].results)
      .enter()
      .append('rect')
      .style('fill', 'red')
      .attr('width', xScale.bandwidth())
      .attr('height', 70)
      .attr('x', function (d) {
        return xScale(d.title);
      })
      .attr('y', function (d) {
        return yScale.bandwidth() + 175;
      });

  }
}).catch(error => console.log(error));

【问题讨论】:

    标签: javascript arrays json object d3.js


    【解决方案1】:

    很好的问题描述和 CodePen - 帮助您轻松!

    我已经对你的CodePen link做了一个轻微修改的版本@

    基本上,发生的情况是,对于 for 循环中的每次迭代,您将选择与 rect 元素匹配的第一个 x(在您的情况下为 20)。这意味着,在第一次迭代之后,每次迭代只会将与当前迭代的 20 个数据条目的数据绑定替换为相同的 20 个rect 元素。

    我做了两个改动来解决这个问题:

    1. svg.selectAll('rect') 更改为svg.selectAll('.rect_${i}'),它选择具有相应类的所有元素(.rect_0.rect_1 等)。这样做可以防止覆盖以前的 rect 元素。
    2. .attr('x')的回调函数中添加了+ xScale.range()[1] * i。这会将每批 20 个 rect 元素向右移动,以确保 rect 元素不会相互重叠。

    然而,第 2 点确实构成了一个非常宽的图表。如果您希望这些行位于彼此下方,我已经添加了功能(在评论中),使用您的 rect 元素的高度来做到这一点。

    如果这对你有帮助,请告诉我!

    【讨论】:

    • 谢谢!但是,这不会生成 2000 条,并且不适合容器。
    • 第一个通道实际上是唯一添加元素的通道,没有任何内容被替换,因为输入选择对于每个后续选择都是空的。
    • @AndrewReid,你是对的 - 它只是被替换/更新为相同矩形的数据绑定。
    • @EdgarKiljak,实际上所有 2000 个条形图都在绘制 - 其中大部分都在 SVG 之外。尽管您想要如此广泛的可视化确实让我感到奇怪(人们总是可以更改 SVG 尺寸),但我认为每个人都有自己的 :)
    【解决方案2】:

    在您的循环中,您仅在第一次通过循环时附加元素:数据数组中有 20 个项目,svg 中没有矩形,因此输入了所有 20 个项目。第二次data数组有20个item,svg中有20个矩形,所以enter选择为空,新数据简单绑定到现有的20个矩形。

    作为另一个答案的替代方法,您的数据似乎不是分层的,但您具有分层结构。如果您的数据结构与您的 DOM 结构相同,那么这将更加直接。让我们稍微重构一下你的数据:

    var combined = [];
    for (let i = 0; i < data.length; i++) {
      combined.push(...data[i].results);
    }
    

    现在我们的数据是:

    [
      {vote_count: 17968, id: 19995, video: false, vote_average: 7.4, title: "Avatar", …,}
      {...},
      {...},
      ...
    

    所有要映射的对象都在一个数组中,数组中的一项,一个要添加到图表的元素。现在我们可以做一个简单的回车循环:

    svg.selectAll('rect')
      .data(combined)
      .enter()
      .append('rect')
      .style('fill', 'red')
      .attr('width', xScale.bandwidth())
      .attr('height', 70)
      .attr('x', function (d) {
        return xScale(d.title);
      })
      .attr('y', function (d) {
        return yScale.bandwidth() + 175;
      });
    

    一般来说,如果您需要一个循环来在 DOM 中输入元素,那么您不会使用惯用的 d3。通过避免使用 for 循环输入元素,缩放也变得更容易,因为我们不会在遍历父数组时不断更新它。比例域和范围只需设置一次。

    这是更新后的codepen

    现在,2000 个元素将产生非常小的矩形,可能小于 1 个像素宽,我不确定你为什么要这样缩放条形的高度,但这些是不同的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-28
      • 2017-01-29
      • 2015-10-15
      相关资源
      最近更新 更多