Javascript – recursively remove nodes of a certain type from tree, but reattach and spread eligible children

Tags: , , ,



I’m writing a recursive function on a JSON tree {name, type, [children]} to remove nodes of a certain type. However, the children of the removed node should get re-attached to the parent, if they are not of the type to be removed.

I’m experiencing the following difficulty: Let’s say I want to remove type b on the following tree:

const sampleData = [{
name: "parent",
type: "a",
children: [{
    name: "childA",
    type: "a",
    children: null
    },{
    name: "childB",
    type: "b",
    children: [{
        name: "grandChildA",
        type: "a",
        children: null
        },{
        name: "grandChildB",
        type: "a",
        children: null
        }]
    },{
    name: "childC",
    type: "a",
    children: null
    }]
}]

The original children for parent is [childA, childB, childC]. After the removal, the parent should have children [childA, grandChildA, grandChildB, childC]. However, the result I’m getting is [childA, [grandChildA, grandChildB], childC].

I know I need to spread it out, but I’m not sure where to do it in the recusion.

Here’s the function that I have right now (I know I’m using the spread syntax in the wrong place):

const removeType = (node, type) => {
    //if the node should not be removed    
    if (node.type !== type){
        //if the node has children, recursively call to prune children
        if (node.children && node.children.length > 0){
            node.children = [...node.children.map(child => removeType(child, type))
                                             .filter(child => child !== null)]
            return node
        }
        //if the node has no children, return the node
        else return node
    }
    //if the node should be removed
    else if (node.type === type){
        //if the node has children, recursively call, then reattach the children
        if (node.children && node.children.length > 0){
            node.children = [...node.children.map(child => removeType(child, type))
                                             .filter(child => child !== null)]
            return node.children
        }
        //
        else return null
    }
}

Answer

Updated

I think you can use reduce for that, I’m not having my computer right now to test it, but it’ll be something like this

const removeType = (node, type) => {
   if (node === null) {
     return null;
   } else {
    return node.reduce((acc, child) => {
      if(child["type"] === type) {
        const removedChild = removeType(child["children"], type);
        acc = [...acc, ...removedChild];
      } else {
        child.children = removeType(child["children"], type);
        acc.push(child);
      }
      return acc;
    }, []);
  }
}

2nd Update

Code reduced:

const removeType = (node, type) => {
    if (!node) return;

    return node.reduce((acc, child) => {
        if(child["type"] === type) {
            const removedChild = removeType(child["children"], type);
            acc = [...acc, ...removedChild];
        } else {
            child.children = removeType(child["children"], type);
            acc.push(child);
        }
        return acc;
    }, []);

}


Source: stackoverflow