Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to effectively filter tree view retaining its existing structure?

I am having a tree structured JSON which should be filtered and the result should retain the tree structure.

var tree = [
  {
    text: "Parent 1",
    nodes: [
      {
        text: "Child 1",
        type: "Child",
        nodes: [
          {
            text: "Grandchild 1"
            type: "Grandchild"
          },
          {
            text: "Grandchild 2"
            type: "Grandchild"
          }
        ]
      },
      {
        text: "Child 2",
        type: "Child"
      }
    ]
  },
  {
    text: "Parent 2",
    type: "Parent"
  },
  {
    text: "Parent 3",
    type: "Parent"
  }
];

Example :

1)If search query is Parent 1

Expected result :

[
  {
    text: "Parent 1",
    nodes: [
      {
        text: "Child 1",
        type: "Child",
        nodes: [
          {
            text: "Grandchild 1"
            type: "Grandchild"
          },
          {
            text: "Grandchild 2"
            type: "Grandchild"
          }
        ]
      },
      {
        text: "Child 2",
        type: "Child"
      }
    ]
  }
]

2)If search query is Child 1

Expected result :

[
  {
    text: "Parent 1",
    nodes: [
      {
        text: "Child 1",
        type: "Child",
        nodes: [
          {
            text: "Grandchild 1"
            type: "Grandchild"
          },
          {
            text: "Grandchild 2"
            type: "Grandchild"
          }
        ]
      }
    ]
  }
]

3)If search query is Grandchild 2

Expected result :

[
  {
    text: "Parent 1",
    nodes: [
      {
        text: "Child 1",
        type: "Child",
        nodes: [
          {
            text: "Grandchild 2"
            type: "Grandchild"
          }
        ]
      }
    ]
  }
]

I need to retain the tree structure based on the node level (type here). So far I have tried filtering recursively but couldn't re-map the results.

angular.module("myApp",[])
   .filter("filterTree",function(){
       return function(items,id){
          var filtered = [];
          var recursiveFilter = function(items,id){
              angular.forEach(items,function(item){
                 if(item.text.toLowerCase().indexOf(id)!=-1){
                    filtered.push(item);
                 }
                 if(angular.isArray(item.items) && item.items.length > 0){
                    recursiveFilter(item.items,id);              
                 }
              });
          };
          recursiveFilter(items,id);
          return filtered;
       }; 
    });
});

My JSON is pretty large and hence remapping based on types are expected to be done in the filter itself. Please advice.

like image 386
Surabhil Avatar asked Jul 24 '17 20:07

Surabhil


1 Answers

You could use a nested recursive approach and filter the tree, while respecting the found item.

This solution does not mutate the original data.

function filter(array, text) {
    const getNodes = (result, object) => {
        if (object.text === text) {
            result.push(object);
            return result;
        }
        if (Array.isArray(object.nodes)) {
            const nodes = object.nodes.reduce(getNodes, []);
            if (nodes.length) result.push({ ...object, nodes });
        }
        return result;
    };

    return array.reduce(getNodes, []);
}

var tree = [{ text: "Parent 1", nodes: [{ text: "Child 1", type: "Child", nodes: [{ text: "Grandchild 1", type: "Grandchild" }, { text: "Grandchild 2", type: "Grandchild" }] }, { text: "Child 2", type: "Child" }] }, { text: "Parent 2", type: "Parent" }, { text: "Parent 3", type: "Parent" }];

console.log(filter(tree, 'Parent 1'));
console.log(filter(tree, 'Child 1'));
console.log(filter(tree, 'Grandchild 2'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 81
Nina Scholz Avatar answered Oct 19 '22 00:10

Nina Scholz