I need based on a condition to remove a node from a tree nodes and obtain as a result an updated copy of the tree nodes
With the help of this answer https://stackoverflow.com/a/72121755/615274 I get to the node to delete, I exclude it from the array it belongs to but the returned node tree does not reflect the changes
The data is as follows
const data = [ { data: 1, children: [ { data: 1000, children: [] }, { data: 1200, children: [] }, ], }, { data: 2, children: [ { data: 10, children: [ { data: 1001, children: [] }, { data: 1201, children: [] }, { data: 1002, children: [] }, { data: 1201, children: [ { data: 111, children: [] }, { data: 222, children: [] }, ], }, ], }, { data: 12, children: [ { data: 100, children: [] }, { data: 120, children: [] }, ], }, ], }, ];
The logic I use now is the following
function deleteNode(treeNode, targetId) { if (treeNode && Array.isArray(treeNode) && treeNode.length > 0) { for (let node of treeNode) { if (node.data === targetId) { treeNode = treeNode.filter((n) => n.data !== targetId); break; } deleteNode(node.children, targetId); } } return treeNode; }
This identifies the node to delete, excludes it from it container, but when returning the node tree, the modification is not reflected.
const data = [ { data: 1, children: [ { data: 1000, children: [] }, { data: 1200, children: [] }, ], }, { data: 2, children: [ { data: 10, children: [ { data: 1001, children: [] }, { data: 1201, children: [] }, { data: 1002, children: [] }, { data: 1201, children: [ { data: 111, children: [] }, { data: 222, children: [] }, ], }, ], }, { data: 12, children: [ { data: 100, children: [] }, { data: 120, children: [] }, ], }, ], }, ]; function deleteNode(treeNode, targetId) { if (treeNode && Array.isArray(treeNode) && treeNode.length > 0) { for (let node of treeNode) { if (node.data === targetId) { treeNode = treeNode.filter((n) => n.data !== targetId); console.log("==== deleted node ====") console.dir(treeNode, { depth: null }); console.log("==== deleted node ====") break; } deleteNode(node.children, targetId); } } return treeNode; } const output = deleteNode(data, 111); console.dir(output, { depth: null });
Thanks in advance
Advertisement
Answer
I regularly use variants of a deepFilter
function, which lets us build a non-mutating version of this very easily:
const deepFilter = (pred) => (xs) => xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilter (pred) (children)) => pred (rest) || kids.length ? [{...rest, ...(kids.length ? {children: kids} : {})}] : [] ) const deleteNode= (target) => deepFilter (node => node .data !== target) const data = [{data: 1, children: [{data: 1e3, children: []}, {data: 1200, children: []}]}, {data: 2, children: [{data: 10, children: [{data: 1001, children: []}, {data: 1201, children: []}, {data: 1002, children: []}, {data: 1201, children: [{data: 111, children: []}, {data: 222, children: []}]}]}, {data: 12, children: [{data: 100, children: []}, {data: 120, children: []}]}]}] console .log (deleteNode (111) (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
deepFilter
tests a given predicate function against each of your input values, and recursively against their children. If it returns true for the value or for any of its children, we keep that value in the result. If not, we skip it.
That lets us write a trivial deleteNode
function, simply by testing whether the node’s data
property is different from our target value.