如何根据父项是否在D3中过滤来过滤子项? [英] How to filter children based on whether the parent is filtered in D3?

查看:752
本文介绍了如何根据父项是否在D3中过滤来过滤子项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用D3中的Zoomable冰柱布局示例来可视化文件夹层次结构。我想根据文件夹是否在某个日期之前访问过的文件夹来隐藏某些文件夹 - 使用过滤器:

I use the Zoomable Icicle layout example in D3 to visualize a folder hierarchy. I would like to hide certain folders based on whether the folder has been accessed before a certain date - which works using filter:

.filter(function (d) {
    return d.dateAccessed > formattedD; //formattedD is the date two weeks ago
})

我需要做的是隐藏已隐藏的父文件夹的子文件夹(子文件夹和文件),如果显示父文件夹则显示子文件。

What I need to do is hide the children (sub-folders and files) of that parent folder that has been hidden OR show the children if the parent is shown.

如何将父项的筛选值指定给子项?

How do I assign a filter value of a parent to its children?

谢谢!

推荐答案

还有一个用于帽子戏法 ...

And one more for the hat trick...

最终选择,我在之后考虑过所有这一切,我认为它是赢家。它不仅更接近你工作的例子,而且适用于任何D3层次布局函数。秘密:得到D3为你做的工作。具体来说:

Final option, that I thought of after all that, and I think it's the winner. Not only is it closer to the example you were working from, but it works for any of the D3 hierarchical layout functions. The secret: get D3 to do the work for you. Specifically:


  1. 使用D3布局函数计算只包含满足过滤条件的节点的新布局;

  2. 对于作为新布局一部分的所有节点,显示它们并更新它们的位置/大小。

  3. 隐藏没有布局数据的节点新布局。

诀窍在步骤1;使布局功能仅包括满足过滤条件的节点。 .children()方法的分区布局函数允许你指定布局函数如何识别孩子。示例中的函数是:

The trick is in step 1; getting the layout function to only include the nodes that meet your filter criteria. The .children() method of the partition layout function allows you to specify how the layout function identifies the children. The function in the example was:

var partition = d3.layout.partition()
    .children(function(d) { return isNaN(d.value) ? d3.entries(d.value) : null; })
    .value(function(d) { return d.value; });

这意味着它只是期望节点包含子元素数组或数字。如果你只想包含一些子元素,你所要做的就是遍历子元素数组并返回你想要的元素:

Meaning it just expects the node to contain an array of child elements or else a number. If you only want some children included, all you have to do is go through the array of child elements and return the ones you want:

var filteredPartition = d3.layout.partition()
    .value(function(d) { return d.value; })
    .children(function(d){

       if isNaN(d.value) {
         var filteredChildren = [];
         d3.entries(d.value).forEach(function(d2){
           if (d2.dateAccessed < formattedD) filteredChildren.push(d);
           //a.push(d) adds d to the end of the array 'a'
         });
         return filteredChildren;
       //Note that the nodes which PASS the filter test will be shown
       //and the nodes that fail will be hidden; make sure your filter
       //is written in the way you want.
       }
       else return null;
    });

当然,这假设一个简单的数据结构是数组数组。对于你的数据结构,你必须更新两个子访问器函数。

Of course, this assumes a simple data structure that is arrays of arrays of numbers. For your data structure you'll have to update both child-accessor functions.

在子访问器函数中应用过滤器的好处是,一旦元素失败了过滤器,它的子子元素也会被自动排除,因为布局函数从来没有看到他们。 (不需要递归函数:D3为你!)

The great thing about applying the filter in the child accessor function, is that once an element fails the filter, it's sub-children are automatically excluded too, since the layout function never even sees them. (No recursive functions required: D3 does it for you!)

要应用新的过滤布局,创建一个更新函数作为参数布局函数: p>

To apply the new filtered layout, create an update function that takes as a parameter the layout function:

var updateLayout(layoutFunction){

    var newRects = rects.data(layoutFunction(rootData), keyFunction)
            .transition().duration(500)
            .style("visibility", "visible")
            .attr("x", function(d) { return x(d.x); })
            .attr("y", function(d) { return y(d.y); })
            .attr("width", function(d) { return x(d.dx); })
            .attr("height", function(d) { return y(d.dy); });

    newRects.exit()
            .transition().duration(500)
            .style("visibility", "hidden");
       //don't delete, just hide;  these will still be part 
       //of the rects selection for the next update.
}

要应用过滤器,请调用 updateLayout(filteredPartition) ;要恢复到未过滤的版本,调用 updateLayout(partition)(其中分区是原始布局函数的名称来自示例)。

To apply the filter, call updateLayout(filteredPartition); to revert back to the unfiltered version, call updateLayout(partition) (where partition was the name of the original layout function from the example).

只剩下几个细节。首先,要让所有的开始,我需要有在原始布局中使用的根数据对象。这意味着它需要在图形首次初始化时保存在变量中。第二,我们需要一个键功能,可以将新的布局数据对象与旧布局的数据对象进行匹配。这里是必要的声明和初始化方法更新为包括它们:

There's only a couple details left. First, to get it all started I needed to have the root data object that was used in the original layout. That means it needed to have been saved in a variable when the graph was first initialized. Second, we need a key function that can match up the new layout data objects with the old layout's data objects. Here's the necessary declarations and the initialization method updated to include them:

var keyFunction = function(d) {

    return d.FileName; 
    //or something else from your data structure that uniquely 
    //identifies each file

    //You could even do it recursively to get the full, unique file path:
    //return (d.parent == null) ? 
    //    "d.FileName" : keyFunction(d.parent) + "/" + d.FileName;
}
var rootData;

d3.json("readme.json", function(error, root) {
  rootData = d3.entries(root)[0];
  rect = rect.data(partition(rootData), keyFunction)
     .enter()
  //...and the rest is the same
}

我不知道这是否是一个简单的解决方案,但与其他两个答案相比,这是很直接的。

I don't know if that quite counts as a straightforward solution, but it's straightforward when compared to the other two answers.

无论如何,如果你实际实现任何或所有这些方法,我很想看到最终产品,如果你能够在线发布。

Anyway, if you actually implement any or all of these approaches, I'd love to see the final product if you're able to post it online.

这篇关于如何根据父项是否在D3中过滤来过滤子项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆