如何根据父级是否在D3中过滤来过滤子级?

sim*_*im1 1 filter d3.js

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

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

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

如何为其子项分配父项的过滤值?

谢谢!

Ame*_*aBR 5

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

最后的选择,我想到以后这一切,我认为这是赢家.它不仅更接近您所使用的示例,而且适用于任何D3分层布局功能.秘诀:让D3为你做的工作.特别:

  1. 使用D3布局函数计算仅包含符合过滤条件的节点的新布局;
  2. 对于属于新布局的所有节点,显示它们并更新它们的位置/大小.
  3. 隐藏新布局中没有布局数据的节点.

诀窍在第1步; 使布局功能仅包含符合筛选条件的节点.分区布局功能的.children()方法允许您指定布局功能如何识别子项.示例中的函数是:

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

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

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;
    });
Run Code Online (Sandbox Code Playgroud)

当然,这假设一个简单的数据结构,即数组数组.对于您的数据结构,您必须更新两个子访问器功能.

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

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

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.
}
Run Code Online (Sandbox Code Playgroud)

要应用过滤器,请致电updateLayout(filteredPartition); 要恢复到未过滤的版本,请调用updateLayout(partition)(partition示例中原始布局函数的名称).

还剩下几个细节.首先,为了让它全部启动,我需要拥有原始布局中使用的根数据对象.这意味着在首次初始化图形时需要将其保存在变量中.其次,我们需要一个能够将新布局数据对象与旧布局的数据对象相匹配的关键函数.这是必要的声明和更新初始化方法以包含它们:

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
}
Run Code Online (Sandbox Code Playgroud)

我不知道这是否算是一个直截了当的解决方案,但与其他两个答案相比,它是直截了当的.

无论如何,如果您实际实施任何或所有这些方法,我很乐意看到最终产品,如果您能够在线发布.