简体   繁体   中英

How to filter JavaScript object based on property condition?

I am working on a d3 visualization to create a sunburst. I am also adding a functionality to search for a particular arc so that the sunburst displays arcs with only the searched label. If the initial object is:

{
    "name": "root",
    "children": [
        {
            "name": "A",
            "color": "red",
            "children": [
                {
                    "name": "B",
                    "color": "red"
                }
            ]
        },
        {
            "name": "C",
            "color": "red",
            "children": [
                {
                    "name": "D",
                    "color": "red"
                }
            ]
        },
        {
            "name": "E",
            "color": "red",
            "children": [
                {
                    "name": "B",
                    "color": "red"
                }
            ]
        }
    ]
}

I would like to filter this such that it returns the whole hierarchy which contains the searched name anywhere in the hierarchy. Below is a sample output required when searched for "name":"B"

{
    "name": "root",
    "children": [
        {
            "name": "A",
            "color": "red",
            "children": [
                {
                    "name": "B",
                    "color": "red"
                }
            ]
        },
        {
            "name": "E",
            "color": "red",
            "children": [
                {
                    "name": "B",
                    "color": "red"
                }
            ]
        }
    ]
}

Please tell me if anything else might be needed. Thank you.

Code snippet of how I tried to filter.

var path = svg.selectAll("path")
           .data(partition.nodes(root))
           .enter()
           .append("path")
           .filter(function(d){ 
               return (d.name=="B");
           })
           .attr("d", arc)
           .style("fill", function(d) {
               console.log(d.name);
               return color((d.children ? d : d.parent).name); 
           })

This returns only "name":"B" arc and not the hierarchy.

If you want the .filter() way to work for you, you will have to flatten your JSON data apply a filter and then unflatten it. You can check out flattening and unflattening if you are interested. I personally prefer the depth-first-search (or bfs also works) and pruning where required approach. Lets assume your json data is present in mydata javascript var:

d3.json("jsonData.json",function(error,jsonData){
  if(error)
    return console.warn(error);
  var mydata = jsonData;
  dfs(mydata,'B');
});

Now your dfs() function will look something like this:

function dfs(data,label){
  if(!('children' in data))
    return data.name==label;
  for(var i=0;i<data.children.length;++i)
    if(dfs(data.children[i],label)==flase)
      data.children.splice(i--,1);
  return data.children.length > 0 || data.name==label;
}

What is happening here is that we are doing a depth-first search of the JSON data. The first IF condition checks if we are at the leaf node level and returns false if the 'name' property is anything but 'B'. Next we traverse all children of the current node, call dfs() for each child and prune that child if it returns false. At the end we return false if all children of current node have been pruned and if the 'name' property of current node is not 'B' (the condition that the non-leaf node also has to be pruned from the hierarchy). Hope this helps. Let me know if I missed something.

My hunch is that you want to color the searching node and its parent in sunburst.

To do this i have made a function which recursively colors a node's parent like this:

function colorGraph(node){

    var sel = svg.selectAll("path").filter(function (d) {
        return (node==d);
    });
    sel.style("opacity", 1);
    //if parent present change its opacity
    if(node.parent){
        colorGraph(node.parent)
    }
}

Full working code here

You will need to pre-filter, rather than doing it within the D3 pipeline.

Define a filter function we will call hasName which checks if a particular object has the desired name, or, recursively, if any of its children have it:

// Does the object contain a specified name, however many levels down?
function hasName(obj, name) {
  return function _hasName(o) {
    return o.name === name || (o.children || []).some(_hasName);
  }(obj);

}

Now filter:

data.children = data.children.filter(function(child) { return hasName(child, 'B'); });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM