【问题标题】:Apply mapping to attached data将映射应用于附加数据
【发布时间】:2018-08-08 14:52:13
【问题描述】:

我有一堆附加到 DOM 元素的数据,我想将每个数据加倍。我天真的方法是将它们全部选中并在选择时使用函数调用数据。我认为这已经足够了,因为数据存储在元素上,而不是选择本身。

但是,这根本不符合我的要求。奇怪的是,它会导致选择数据[undefined] 而不是[2, 4, 6],并且不会影响附加到元素的数据。更奇怪的是,如果我用身份函数调用它,我会得到一个异常:Cannot read property 'length' of undefined

这里发生了什么? D3 是否只允许使用 enter/exit 选择上的函数调用 data

// Create a bunch of elements with attached data.
const selection = d3.selectAll('p').data([1,2,3]).enter().append('p');
console.log(selection.data());

// This doesn't work, but seems like it should:
selection.data(d => d * 2);
console.log(selection.data());

// This throws an exception??
selection.data(d => d);
console.log(selection.data());

// Is this the simplest way that works?
selection.data(selection.data().map(d => d * 2));
console.log(selection.data());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>

【问题讨论】:

    标签: d3.js


    【解决方案1】:

    你的前两个例子很有趣。 虽然第一个示例似乎是一个可行的解决方案:

    selection.data(d => d * 2);
    console.log(selection.data());
    

    它不会按预期工作。首先,.data 需要一个数组,但看起来好像您正在尝试单独传递每个项目。而且,这里的d 是未定义的,传递给.data() 的函数只被调用一次,而不是针对选择中的每个元素。所以在这个例子中没有单独的数据可以使用(你可以在嵌套选择中使用一个数据(参见docs),我只是将上面的内容更改为记录d 我们可以看到它是未定义的:

    // Create a bunch of elements with attached data.
    const selection = d3.selectAll('p').data([1,2,3]).enter().append('p');
    
    // This doesn't work, but seems like it should:
    selection.data(function(d) { console.log(d); return d * 2; })
    console.log(selection.data());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

    为什么第二个例子会抛出错误而第一个没有?当将 undefined 乘以 2 时,我们得到 NaN,在 data 方法中,该方法看起来确定输入数据数组的长度,NaN 长度未定义,未定义长度是错误,我相信这很可能是为什么会产生错误和其他没有:

    console.log(NaN.length);
    console.log((undefined * 2).length);
    console.log(undefined.length);

    无效长度也可能解释了为什么没有创建退出选择并且数据没有更新:退出选择的大小基于输入数据数组的大小。第一个示例中的数据保持不变,因为在 d3-selection 中,更新数据的 for 循环永远不会循环遍历数据,因为永远不会满足 for 条件:

    for (var i = 0; i < dataLength; ++i) { // bind data
    

    D3 更倾向于将(新的或更新的)数据绑定到元素,而不是直接操作绑定到这些元素的数据。但是您可以实现您想要的结果,如您的第三个选项中所示,或者从 Xavier 的评论开始,或者您可以编写一个简单的函数并将其传递给 selection.each() (尽管还有许多其他解决方案):

    // Create a bunch of elements with attached data.
    const selection = d3.selectAll('p').data([1,2,3])
      .enter()
      .append('p')
      .each(double);
    
    function double() {
      d3.select(this).datum(d => d*2);
    }
    
    console.log(selection.data());
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"&gt;&lt;/script&gt;

    【讨论】:

    • 如果我理解正确,当data 使用函数调用时,它会应用于绑定到选择的父项并存储在选择中的数据,而当datum 使用函数调用时,它会被应用到(并覆盖)绑定到选择本身的数据。所以我的最后一个例子可以简化为selection.datum(d =&gt; d * 2)。谢谢!
    • 是的,这听起来很准确。尽管出于某种原因,我忽略了 selection.datum 的明显之处,而是使用了 selection.each 中额外复杂的步骤 - 我想我更关注为什么前两个示例会产生不同的结果。我稍后会更新答案。
    猜你喜欢
    • 2019-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多