如何按指定索引拆分字符串节点数组?求高效嵌套实现方案
嵌套文本节点的高效分割实现问题
我必须沿用现有嵌套数据结构,需要实现一个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' } ] } ] } ]
高效解决方案思路
核心是一次遍历定位节点路径,再基于路径生成三部分数据,避免多次全量遍历:
- 用递归辅助函数找到目标节点的完整路径(包含顶层数组索引、所有父节点链、节点自身);
- 基于路径分别构建
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
相关产品推荐
相关产品推荐

