TypeScript递归删除数组指定条件组问题求助
树形条件组递归删除失效问题排查与修复
问题背景
需要实现树形结构条件组的删除功能:点击X按钮删除指定的整个分组,但现有递归删除代码无法完成删除操作,尽管执行时能读取到待修改的conditions数组。
数据结构
对应的TypeScript对象结构如下:
{ nodeName: 'and', nodeID: 1, parentNodeID: null, conditions: [ { children: [], displayDataField: 'Cliente.Centro.Centro', order: 1, value1: '1', parentNodeID: 1, }, { order: 2, dataField: null, condition: null, value: null, parentNodeID: 1, children: [ { nodeName: 'or', nodeID: 2, parentNodeID: 1, conditions: [ { children: [], displayDataField: 'Cliente.Datos.Código cliente', order: 1, value1: '333', parentNodeID: 2, }, { children: [], displayDataField: 'Cliente.Datos.Código cliente', order: 2, value1: '44', parentNodeID: 2, }, { order: 3, dataField: null, condition: null, value: null, parentNodeID: 2, children: [ { nodeName: 'or', nodeID: 3, parentNodeID: 2, conditions: [ { children: [], displayDataField: 'Cliente.Datos.Código cliente', order: 1, value1: '1', parentNodeID: 3, }, { children: [], displayDataField: 'Cliente.Datos.Código cliente', order: 2, value1: '30000', parentNodeID: 3, }, ], }, ], }, ], }, ], }, { children: [], displayDataField: 'Cliente.Datos.Fecha de nacimiento', order: 3, value1: '01/02/1980', parentNodeID: 1, }, ], };
现有代码
当前编写的删除逻辑代码:
onDeleteConditionGroup(eventInfo: { e: any; condition: QueryConditionDto }) { this.queryGroupService.deleteConditionGroup(this.globalQuery.query.conditions, eventInfo.condition.parentNodeID); } deleteConditionGroup(query: QueryConditionDto[], nodeID: number) { this._found = false; this._queryModified = []; this._deleteConditionGroup(query, nodeID); } private _deleteConditionGroup(query: QueryConditionDto[], nodeID: number, found: boolean = false) { query.forEach((item: QueryConditionDto) => { if (!found) { if (this.queryService.isGroupCondition(item) && item.children[0].nodeID == nodeID) { const index = query.findIndex((condition: QueryConditionDto) => condition.children[0]?.nodeID == nodeID); // We must choose the preconditions and postconditions // The conditions that follow must update their order attribute. if (index > -1) { const conditionsPrevious = query.filter((condition: QueryConditionDto) => condition.order < query[index].order); const conditionsBack = query.filter((condition: QueryConditionDto) => condition.order > query[index].order); conditionsBack.forEach((condition: QueryConditionDto) => { condition.order = condition.order - 1; }); // We keep the conditions except for the deleted one. BUT IT DOESN'T WORK IT DOESN'T REMOVE ME WTF?!? query = conditionsPrevious.concat(conditionsBack); this._found = true; } } else if (this.queryService.isGroupCondition(item) && item.children[0].nodeID != nodeID && !this._found) { this._deleteConditionGroup(item.children[0].conditions, nodeID); } } }); }
问题分析
- 数组引用未修改:
query = conditionsPrevious.concat(conditionsBack)只是给局部变量query重新赋值,并没有修改传入的原数组的内容,原数组完全没变化,这是删除失效的核心原因。 - 遍历逻辑缺陷:使用
forEach循环时,即使找到目标并标记_found = true,循环仍会继续执行;同时依赖全局变量_found控制递归,容易出现状态不一致的问题。 - 目标定位逻辑问题:判断条件
item.children[0].nodeID == nodeID不够严谨,且递归进入子节点时的逻辑没有覆盖所有可能的嵌套场景。
修复方案
修改后的递归删除代码如下,核心改动点已标注:
onDeleteConditionGroup(eventInfo: { e: any; condition: QueryConditionDto }) { this.queryGroupService.deleteConditionGroup(this.globalQuery.query.conditions, eventInfo.condition.parentNodeID); } deleteConditionGroup(query: QueryConditionDto[], nodeID: number) { // 直接调用递归函数,用返回值判断是否找到并删除 this._deleteConditionGroup(query, nodeID); } private _deleteConditionGroup(query: QueryConditionDto[], nodeID: number): boolean { // 使用for循环,方便找到目标后立即终止遍历 for (let i = 0; i < query.length; i++) { const item = query[i]; if (this.queryService.isGroupCondition(item)) { // 先检查当前item是否是要删除的组的容器 if (item.children[0]?.nodeID === nodeID) { // 1. 直接用splice修改原数组,删除目标项 query.splice(i, 1); // 2. 更新后续项的order值 for (let j = i; j < query.length; j++) { query[j].order -= 1; } // 返回true表示已找到并删除 return true; } // 递归进入子条件组,检查是否在子节点中找到目标 const foundInChild = this._deleteConditionGroup(item.children[0].conditions, nodeID); if (foundInChild) { return true; } } } // 未找到目标,返回false return false; }
关键改动说明
- 直接修改原数组:使用
array.splice()方法直接操作传入的数组,确保原数组的内容被修改,解决了之前局部变量赋值无效的问题。 - 递归返回值控制:递归函数返回布尔值,标记是否找到并删除了目标,避免依赖全局变量,逻辑更清晰可靠。
- for循环替代forEach:使用for循环可以在找到目标后立即终止遍历,提升效率,同时避免forEach循环中修改数组导致的遍历异常。
- 简化order更新逻辑:删除目标项后,直接遍历后续的项更新order,比之前的filter+concat更高效且直观。
内容的提问来源于stack exchange,提问作者Stella Esparza
相关产品推荐
相关产品推荐

