Skip to content
Advertisement

how to get an updated copy of a node tree after removing some of its nodes?

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.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement