递归构建数组函数异常求助:仅返回顶层节点问题排查
递归函数修复方案及问题分析
看起来你的递归函数在结果累加和节点处理上存在几个关键问题,导致最终结果不符合预期。我来一步步拆解并修复:
原函数的核心问题
- 遗漏当前节点ID:函数完全没有将传入的
$node的ID加入结果集,这直接导致顶层节点(你传入的Top节点)不会出现在最终结果里(如果你的需求是包含它的话)。 - 结果集被覆盖而非累加:每次循环中你都用
$results = array_merge($nodes,$children);直接赋值,这会把前一次循环得到的子节点结果完全覆盖。比如处理完UKDept及其子节点后,结果是[33,36],但处理USDept时,这个结果会被替换成[33,37],导致UKSubDept的ID丢失。 - 变量命名冲突:循环内复用了
$children变量,覆盖了外层的子节点集合,虽然不会直接破坏foreach迭代,但会让代码逻辑混乱,增加调试难度。 - 冗余的
$nodes变量:这个变量的存在让结果合并逻辑变得复杂,其实可以直接通过递归返回的结果来累加。
修复后的函数代码
private static function traverseTree($tree, $node) { // 先把当前节点的ID加入结果集(如果不需要当前节点,可删除这一行) $results = [$node->id]; // 获取当前节点的所有子部门 $children = $tree->where('parent_id', $node->id)->where('type', UserGroup::DEPARTMENT_TYPE); foreach($children as $child) { // 递归获取子节点的所有子孙ID,并追加到当前结果中 $results = array_merge($results, self::traverseTree($tree, $child)); } return $results; }
修复逻辑说明
- 包含当前节点:初始化结果集时直接加入
$node->id,确保顶层节点和所有子节点都被包含(如果你的需求是仅获取子节点,删除这一行即可)。 - 累加结果而非覆盖:每次递归调用后,用
array_merge将子节点的结果追加到当前结果集里,这样所有分支的节点都会被保留。 - 简化逻辑:去掉了冗余的
$nodes变量,直接通过递归返回值来构建结果,代码更清晰易读。
优化建议(针对大数据量)
如果你的节点数量较多,每次递归调用都用where遍历整个Collection效率会很低。可以提前将Collection按parent_id分组,减少重复遍历:
// 在调用递归函数前先预处理树结构 $groupedTree = $tree->groupBy('parent_id'); // 修改后的递归函数 private static function traverseTree($groupedTree, $node) { $results = [$node->id]; // 直接通过分组后的结构获取子节点,无需遍历整个集合 if ($groupedTree->has($node->id)) { $children = $groupedTree->get($node->id)->where('type', UserGroup::DEPARTMENT_TYPE); foreach($children as $child) { $results = array_merge($results, self::traverseTree($groupedTree, $child)); } } return $results; }
用这个优化后的版本,查找子节点的速度会大幅提升,尤其当节点数量超过几十上百个时。
内容的提问来源于stack exchange,提问作者Mick
相关产品推荐
相关产品推荐

