如何在删除某些节点后获取节点树的更新副本?

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

我需要根据条件从树节点中删除一个节点,并因此获得树节点的更新副本

在这个答案的帮助下我到达要删除的节点,我将它从它所属的数组中排除,但返回的节点树没有反映更改

数据如下

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);
                break;
            }
            deleteNode(node.children, targetId);
        }
    }

    return treeNode;
}

这标识要删除的节点,将其从容器中排除,但在返回节点树时,不会反映修改。

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 });

提前致谢

问题通过以下方式解决

function deleteNode(nodes, targetId) {
    if (nodes && Array.isArray(nodes) && nodes.length > 0) {
        nodes.forEach((node, index) => {
            if (node.data === targetId) {
                nodes.splice(index, 1)
            }

            deleteNode(node.children, targetId)
        });
    }

    return nodes;
}

主要的变化是,现在当我删除元素时,我使用 splice 函数来修改原始数组。并且因为作为数组的对象作为引用传递给函数,所以函数内部所做的每个更改都会影响原始数组。

可以简化成这样

function deleteNode(nodes, targetId) {
    if (nodes && Array.isArray(nodes) && nodes.length > 0) {
        nodes.forEach((node, index) => {
            if (node.data === targetId) {
                nodes.splice(index, 1)
            }

            deleteNode(node.children, targetId)
        });
    }
}

我经常使用 deepFilter 函数的变体,这让我们可以很容易地构建一个 non-mutating 版本:

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 针对您的每个输入值测试给定的谓词函数,并递归地针对它们的子项。如果它 returns 对于该值或其任何子项为真,我们将该值保留在结果中。如果没有,我们跳过它。

这让我们可以编写一个简单的 deleteNode 函数,只需测试节点的 data 属性 是否与我们的目标值不同。