Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
    }
}
like image 353
Samson Liu Avatar asked Nov 18 '25 23:11

Samson Liu


2 Answers

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;
    }, []);

}
like image 55
Abdel P. Avatar answered Nov 20 '25 13:11

Abdel P.


I think this answer is just enough different from the others supplied to add it as an alternative. It has the same recursive structure as the answer from Thankyou, but it makes the simplifying assumption that your input is always an array, as are all non-nil children nodes.

const removeType = (node, target) =>
  node .flatMap (({type, children, ...rest}) =>
    type === target
      ? children ? removeType (children, target) : []
      : [{...rest, type, children: children && (removeType (children, target))}]
  )

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}]}]

console .log (
 removeType (sampleData, 'b')
)
.as-console-wrapper {min-height: 100% !important; top: 0}

I'm not claiming this as an improvement on the other answers, but it is an interesting alternative.

like image 44
Scott Sauyet Avatar answered Nov 20 '25 14:11

Scott Sauyet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!