PHP递归提取父子数据结构完整分支问题排查
问题:从层级对象数组中提取节点及其所有父节点
场景与示例数据
从SQL查询得到一个对象数组,每个对象包含id、type、title、parent_id字段,示例数据如下:
$array = array( 0 => (object) array( 'id' => 1, 'type' => 'label', 'title' => 'Product Categories', 'parent_id' => 0, ), 1 => (object) array( 'id' => 2, 'type' => 'label', 'title' => 'Shoes', 'parent_id' => 1, ), 2 => (object) array( 'id' => 3, 'type' => 'label', 'title' => 'T-Shirts', 'parent_id' => 1, ), 3 => (object) array( 'id' => 4, 'type' => 'label', 'title' => 'With Print', 'parent_id' => 2, ), 4 => (object) array( 'id' => 5, 'type' => 'label', 'title' => 'Without Print', 'parent_id' => 2, ), 5 => (object) array( 'id' => 6, 'type' => 'label', 'title' => 'Brands', 'parent_id' => 2, ), 6 => (object) array( 'id' => 7, 'type' => 'label', 'title' => 'Blue', 'parent_id' => 3, ), 7 => (object) array( 'id' => 8, 'type' => 'label', 'title' => 'Red', 'parent_id' => 3, ) );
期望输出
根据指定ID(如7),提取该节点及其所有父节点组成数组,示例预期结果:
Array ( [0] => stdClass Object ( [id] => 7 [type] => "label" [title] => "Blue" [parent_id] => 3 ) [1] => stdClass Object ( [id] => 3 [type] => "label" [title] => "T-Shirts" [parent_id] => 1 ) [2] => stdClass Object ( [id] => 1 [type] => "label" [title] => "Product Categories" [parent_id] => 0 ) )
当前问题
调用原递归函数后,仅返回起始ID对应的条目,以ID7为例,实际输出:
array ( 0 => (object) array( 'id' => 7, 'type' => 'label', 'title' => 'Blue', 'parent_id' => 3, ), )
原函数逻辑问题
原递归函数存在两处关键错误:
- 循环中每次匹配失败就将
$next_id设为false,若目标节点不在数组起始位置,后续循环会覆盖正确的匹配状态,导致无法触发递归 - 当
parent_id为0时,$next_id != false的判断会把0视为布尔值false,直接终止递归,无法获取根节点
修正方案
方案1:修复递归逻辑
调整循环逻辑,先找到目标节点再处理父ID,同时修正终止条件:
function getParentSelect($list, $targetId) { $result = []; // 遍历数组找到当前目标节点 foreach ($list as $item) { if ($item->id == $targetId) { $result[] = $item; // 父ID不为0时,递归查找父节点 if ($item->parent_id !== 0) { $parentNodes = getParentSelect($list, $item->parent_id); $result = array_merge($result, $parentNodes); } break; } } return $result; } // 调用示例 $branch = getParentSelect($array, 7);
方案2:预处理映射优化效率
如果数组数据量较大,推荐先将数组转为以id为键的映射表,避免每次递归遍历整个数组,提升查找效率:
// 预处理数组,构建ID到对象的映射 function buildIdMap($list) { $idMap = []; foreach ($list as $item) { $idMap[$item->id] = $item; } return $idMap; } // 提取节点及其父节点 function getParentSelect($idMap, $targetId) { $result = []; $currentId = $targetId; // 循环查找直到节点不存在或父ID为0 while (isset($idMap[$currentId])) { $result[] = $idMap[$currentId]; $currentId = $idMap[$currentId]->parent_id; // 父ID为0时终止循环(若根节点ID为0可根据需求调整) if ($currentId === 0) { break; } } return $result; } // 调用示例 $idMap = buildIdMap($array); $branch = getParentSelect($idMap, 7);
内容的提问来源于stack exchange,提问作者Change
相关产品推荐
相关产品推荐

