1

I am having a tree structured data 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"
  }
];

I want to filter the tree. I am using this method:

function filter(array, text) {
    return array.filter(function iter(o) {
        var temp;
        if (o.text === text) {
            return true;
        }
        if (!Array.isArray(o.nodes)) {
            return false;
        }
        temp = o.nodes.filter(iter);
        if (temp.length) {
            o.nodes = temp;
            return true;
        }
    });
}

It is working perfectly fine. This question has been previously asked on this Stack Overflow link.

Although, I have one use case that if the grandchild (only in case of type grandchild and not in child or parent) text is matched then the siblings node should also be returned along with their respective parent node. Answer may be simple, but somehow I am not able to think how to do that.

So if I pass this: Grandchild 2, the expected output should be:

[
  {
    text: "Parent 1",
    nodes: [
      {
        text: "Child 1",
        type: "Child",
        nodes: [
          {
            text: "Grandchild 1"
            type: "Grandchild"
          },
          {
            text: "Grandchild 2"
            type: "Grandchild"
          }
        ]
      }
    ]
  }
]
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Dungeon
  • 972
  • 2
  • 16
  • 32

2 Answers2

3

You could take a non mutating approach by ching if the wanted object is in the array, then get all items from the array, otherwise get the next level and add the object if the children is in the next levels.

function filter(array, key, value) {
    return array.some(o => o[key] === value && o.type === 'Grandchild')
        ? array
        : array.reduce((r, o) => {
            if (o[key] === value) return [...r, o];
            if (o.nodes) {
                const nodes = filter(o.nodes, key, value);
                if (nodes.length) r.push({ ...o, nodes });
            }
            return r;
        }, []);
}

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, 'text', 'Grandchild 2'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Hi @NinaScholz, sorry this is delayed.I have mentioned in the question itself that all sibling nodes should only be returned only in case of type `Grandchild`. But in the answer if I supply `Parent 1` all objects are getting returned. Can you please have a look at this. Thanks! – Dungeon Apr 14 '20 at 14:46
1

Write helper method to first find the object which contained the criteria of having given grand child text, then update the found object using filter on nodes.

const filter = (data, word) => {
  const criteria = x => x.nodes?.find(({ text }) => text === word);
  const found = data.find(item => {
    if (!item.nodes) {
      return false;
    }
    return item.nodes.find(criteria);
  });
  return found ? [{ ...found, nodes: found.nodes.filter(criteria) }] : null;
};

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, "Grandchild 2"));
Siva K V
  • 10,561
  • 2
  • 16
  • 29