循环遍历 D3 中的数组数组

Posted

技术标签:

【中文标题】循环遍历 D3 中的数组数组【英文标题】:Loop through array of arrays in D3 【发布时间】:2016-06-14 04:44:53 【问题描述】:

我有一个 x 对象数组,每个对象都有一个 y 对象数组,如下所示:

var data = [
     name: '1', data: [1,2,3] ,
     name: '2', data: [1,2,3] 
]

有没有办法链接 d3 函数以遍历每个 data[x] 和每个 data[x][y]?

我认为这样的事情会有所帮助,但显然不是:

var svg = d3.selectAll('g')
    .data(data)
    .enter()
    .append('g')
    .selectAll('rect') /* This might be made up, but it makes sense to me :)*/
    .data(data, function (d, i) 
        return data[i].data;
    )
    .enter()
    .append('rect')

谢谢

【问题讨论】:

我现在唯一的另一种选择是使用 for 循环,效果很好,只是希望有更聪明的方法 【参考方案1】:

这些方面的东西怎么样://未经测试

var svg = d3.selectAll('g')
    .data(data)
    .enter()
    .append('g')
    .selectAll('rect') /* This might be made up, but it makes sense to me :)*/
    //.data(data, function (d, i) 
    //   return data[i].data;
    //)
    //.enter()
    //.append('rect')
    .each(function(d)
    d3.select(this).selectAll('rect').data(d.data).append('rect')//and so on
    )

编辑:

工作小提琴:http://jsfiddle.net/reko91/58WJE/34/

var data = [
     name: '1', data: [1,2,3] ,
     name: '2', data: [1,2,3] 
]

var colourScale = ['red','blue','yellow']
var svg = d3.select('body').append('svg').attr('width', 1000).attr('height', 1000);
svg.selectAll('rect')
    .data(data)
  .enter().append('g')
    .each(function(d,i)
        d3.select(this).selectAll('rect')
        .data(d.data)
      .enter().append('rect')
        .attr('x', function(d,j)  return j*100; )
        .attr('y', function()  return i*100; )
        .attr('width', function()  return 90; )
        .attr('height', function()  return 90; )
        .attr('fill', function(d,j)  console.log(d); return colourScale[j]; )
    );
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

【讨论】:

是的,感谢each 函数可以解决问题,尽管它必须移动到... append('g').each,因为rect 尚不可用 再次编辑答案。现在工作正常,没问题吧? Yip :) 我觉得第一次还不错 是的,矩形第一次附加很好,但你看不到它们,所以我必须正确定位它们:)【参考方案2】:

虽然明确的.each 循环有效,但d3 认可的方法是使用nested selection:

var data = [
     name: '1', data: [1,2,3] ,
     name: '2', data: [1,2,3] 
]

var colourScale = ['red','blue','yellow']
var colourScale2 = ['yellow','pink','black']
var svg = d3.select('body').append('svg').attr('width', 1000).attr('height', 1000);
svg.selectAll('rect')
    .data(data)
  .enter().append('g')
  .selectAll('rect')
  .data(function(d)
    return d.data;
  )
  .enter()
  .append('rect')
  .attr('x', function(d,i)  return i*100; )
  .attr('y', function(d,i,j)  return j*100; )
  .attr('width', function()  return 90; )
  .attr('height', function()  return 90; )
  .attr('fill', function(d,i)  return colourScale2[i]; )
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

【讨论】:

【参考方案3】:

这是一个关于“How to nest elements with nested data in d3.js”的好问题,我最近也遇到过。 (也在思考一种处理这些情况的稳健方法)

这些由 Mike Bostock 撰写的与该问题相关的文章: 1.How selection work - 让您知道数据如何绑定到选择 2.Nested Selection - 了解选择链接功能是如何在幕后真正发生的

这是我在使用svg时如何处理这个问题的(canvas可能有其他方法)。

首先,D3 的思维模式是“数据驱动文档”。要处理嵌套数据,您必须在 SVG 中创建嵌套元素。(您会发现数据结构的设计非常重要,它反映了结果)。 SVG中的元素(g代表分组)可以解决这个问题,它是为对svg下的等元素进行分组而创建的。你不能在 path、rect、circle 下嵌套元素,但 g 或 svg。

第二,你需要想象一下文档上的嵌套结构。例如:

<svg>
    <g class="layer1">
        <g class="layer2">
            <rect x="0" y="1" class='layer2'></rect>
        </g>
        <g class="layer2">
            <rect x="1" y="1" class='layer2'></rect>
        </g>
        <g class="layer2">
            <rect x="2" y="1" class='layer2'></rect>
        </g>
    </g>
    <g class="layer1">
        <g class="layer2">
            <rect x="0" y="2" class='layer2'></rect>
        </g>
        <g class="layer2">
            <rect x="1" y="2" class='layer2'></rect>
        </g>
        <g class="layer2">
            <rect x="2" y="2" class='layer2'></rect>
        </g>
    </g>
</svg>

第三,使用d3.collection和d3.select来匹配数据和文档。最好阅读 d3 文档,以防不同 d3 版本下的语法发生变化。 d3.v4 和 v3 在数据连接、选择、数据索引、数据操作方面有很大不同,现在 2018/02 d3 进入 v5。 这里重新构建问题代码。

const svg = d3.select('body')
              .append('svg')
              .attr('width', 1000)
              .attr('height', 1000);

我们得到了svg,即d3.select obj,包含svg元素。

const layer1 = svg.selectAll('g.layer1')
                  .data(data)
                  .enter()
                  .append('g')
                  .classed('layer1', true);

我们得到了layer1,它是使用selectAll创建的,用.data()和enter()输入我们的数据,还根据对象的数据数追加g元素,即2在这种情况下

const layer2 = layer1.selectAll('g.layer2')
                     .data((d,i,j)=>
                        let num = d3.entries(j)[i].key
                        return d.data.map((d)=> [d,num])
                                     )
                     .enter()
                     .append('g')
                     .classed('layer2', true);

我们得到了 layer2 元素,利用 d3.selection 在链式函数中的分层选择能力,并使用 d3.enteries 将 layer1 索引传递给 layer2(从 v4 开始,数据键函数将不再保留父索引,所以这是我想出的替代方法。参考这个discussion)。 d3.enteries 给出数组中每个元素的键,我们用它来存储父索引

layer2.append('rect')
      .attr('x', (d,i)=> i*60)
      .attr('y', (d,i)=> d[1]*60)
      .attr('width', 50)
      .attr('height', 50)
      .attr('fill', 'blue');

然后,使用 layer2 元素,我们可以根据 data.data 中的数组和每个数据元素的索引信息附加 rect 元素。顺便说一下,在这个解决方案中,我们让每个元素在data.data中携带父索引。

所以结果可能如下: enter image description here

? ? ?
? ? ?  

Observable 演示:https://beta.observablehq.com/@weitinglin/simple-case-01-nested-data-with-nested-selection

【讨论】:

以上是关于循环遍历 D3 中的数组数组的主要内容,如果未能解决你的问题,请参考以下文章

遍历对象数组以生成 d3 桑基图数据

PHP——数组中的each(),list()和while循环遍历数组

怎样用for循环动态遍历json数组

如何循环遍历数组并取消设置数组中的变量

循环遍历 Bash 中的字符串数组?

在VBA中如何遍历数组中的每个元素?