循环遍历 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中的
第二,你需要想象一下文档上的嵌套结构。例如:
<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 中的数组数组的主要内容,如果未能解决你的问题,请参考以下文章