You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按指定索引拆分字符串节点数组?求高效嵌套实现方案

嵌套文本节点的高效分割实现问题

我必须沿用现有嵌套数据结构,需要实现一个split函数,将数据按指定节点和索引分割为before、range、after三部分,要求保留原嵌套结构且避免低效的三次循环方案,恳请各位提供建议。

当前数据结构

const data = [
    {
        id: 'node-1',
        type: 'text',
        data: 'Hello,'
    },
    {
        id: 'node-2',
        type: 'text',
        data: [
            {
                id: 'node-3',
                type: 'text',
                data: ' world.'
            }
        ]
    },
    {
        id: 'node-4',
        type: 'text',
        data: [
            {
                id: 'node-5',
                type: 'text',
                data: [
                    {   
                        id: 'node-6',
                        type: 'text',
                        data: 'Foo bar'
                    }
                ]
            }
        ]
    }
]

需求效果

实现split函数,调用示例及输出如下:

function split(arr, start_node, start_index, end_node, end_index) { ... }

const { before, range, after } = split(data, 'node-3', 3, 'node-6', 3)

// before 输出
[
    {
        id: 'node-1',
        type: 'text',
        data: 'Hello,'
    },
    {
        id: 'node-2',
        type: 'text',
        data: [
            {
                id: 'node-3',
                type: 'text',
                data: ' wo'
            }
        ]
    },
]

// range 输出
[
    {
        id: 'node-2',
        type: 'text',
        data: [
            {
                id: 'node-3',
                type: 'text',
                data: 'rld.'
            }
        ]
    },
    {
        id: 'node-4',
        type: 'text',
        data: [
            {
                id: 'node-5',
                type: 'text',
                data: [
                    {   
                        id: 'node-6',
                        type: 'text',
                        data: 'Foo'
                    }
                ]
            }
        ]
    }
]

// after 输出
[
    {
        id: 'node-4',
        type: 'text',
        data: [
            {
                id: 'node-5',
                type: 'text',
                data: [
                    {   
                        id: 'node-6',
                        type: 'text',
                        data: ' bar'
                    }
                ]
            }
        ]
    }
]

高效解决方案思路

核心是一次遍历定位节点路径,再基于路径生成三部分数据,避免多次全量遍历:

  1. 用递归辅助函数找到目标节点的完整路径(包含顶层数组索引、所有父节点链、节点自身);
  2. 基于路径分别构建before、range、after,每个部分只处理需要的节点分支,深拷贝避免修改原数据。

代码实现

// 辅助函数:递归查找节点并返回路径信息
function findNodePath(arr, targetId, parentPath = []) {
    for (let i = 0; i < arr.length; i++) {
        const node = arr[i];
        const currentPath = [...parentPath, { topIndex: i, node }];
        if (node.id === targetId) {
            return currentPath;
        }
        if (Array.isArray(node.data)) {
            const found = findNodePath(node.data, targetId, currentPath);
            if (found) return found;
        }
    }
    return null;
}

// 辅助函数:深拷贝节点,避免修改原数据
function deepCopy(node) {
    const copy = { ...node };
    if (Array.isArray(copy.data)) {
        copy.data = copy.data.map(deepCopy);
    }
    return copy;
}

// 核心split函数
function split(arr, startNodeId, startIndex, endNodeId, endIndex) {
    const startPath = findNodePath(arr, startNodeId);
    const endPath = findNodePath(arr, endNodeId);
    if (!startPath || !endPath) throw new Error('目标节点不存在');

    const startTopIndex = startPath[0].topIndex;
    const endTopIndex = endPath[0].topIndex;

    // 构建before部分
    const before = arr.slice(0, startTopIndex + 1).map(deepCopy);
    let currentBeforeNode = before[startTopIndex];
    for (let i = 1; i < startPath.length - 1; i++) {
        currentBeforeNode = currentBeforeNode.data[startPath[i].topIndex];
    }
    currentBeforeNode.data = currentBeforeNode.data.slice(0, startIndex);

    // 构建range部分
    const range = arr.slice(startTopIndex, endTopIndex + 1).map(deepCopy);
    // 处理起始节点剩余内容
    let currentRangeStartNode = range[0];
    for (let i = 1; i < startPath.length - 1; i++) {
        currentRangeStartNode = currentRangeStartNode.data[startPath[i].topIndex];
    }
    currentRangeStartNode.data = currentRangeStartNode.data.slice(startIndex);
    // 处理结束节点截断内容
    let currentRangeEndNode = range[range.length - 1];
    for (let i = 1; i < endPath.length - 1; i++) {
        currentRangeEndNode = currentRangeEndNode.data[endPath[i].topIndex];
    }
    currentRangeEndNode.data = currentRangeEndNode.data.slice(0, endIndex);

    // 构建after部分
    const after = arr.slice(endTopIndex).map(deepCopy);
    let currentAfterNode = after[0];
    for (let i = 1; i < endPath.length - 1; i++) {
        currentAfterNode = currentAfterNode.data[endPath[i].topIndex];
    }
    currentAfterNode.data = currentAfterNode.data.slice(endIndex);

    return { before, range, after };
}

// 测试调用
const { before, range, after } = split(data, 'node-3', 3, 'node-6', 3);
console.log('before:', before);
console.log('range:', range);
console.log('after:', after);

方案优势

  • 仅需一次遍历定位两个节点,后续处理都是基于路径的局部操作,时间复杂度远低于三次全量遍历;
  • 通过深拷贝保证原数据不被修改;
  • 路径记录的方式清晰处理嵌套结构,适配任意深度的节点分割。

内容的提问来源于stack exchange,提问作者Andrew Rusinas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 15:47:50