PHP:遍历数组未按预期生成多层级结构数组问题
嘿,我懂你现在的困扰——你想把扁平的员工关联数组转换成带多层嵌套children的层级结构,但之前写的代码只能生成一级子节点,没法实现深层的嵌套关系。咱们来搞定这个问题!
核心思路
你的反转数组思路是对的(从底层员工往上处理),但关键问题是没有使用引用操作。如果只是直接修改原数组里的经理节点,当经理本身已经是某个上级的子节点时,你修改的只是原数组里的副本,而不是上级节点里的那个子对象。用引用的话,所有层级的修改都会联动更新,自然形成嵌套结构。
方法一:基于引用的迭代法(高效推荐)
这种方法只需要遍历数组两次,效率很高,适合处理大量数据:
// 假设你的原始数组是 $holding $temp = []; $result = []; // 第一步:把所有员工存入临时数组,存储每个员工的引用,方便快速查找 foreach ($holding as &$employee) { $temp[$employee['number']] = &$employee; // 提前初始化children数组,避免后续操作报错 $employee['children'] = []; } unset($employee); // 释放引用,防止后续代码意外修改 // 第二步:遍历临时数组,把每个员工挂载到直属经理的children列表中 foreach ($temp as $employee) { $managerNum = $employee['line_manager_number']; // 如果直属经理存在于数组中,就把当前员工加入经理的children if (isset($temp[$managerNum])) { $temp[$managerNum]['children'][] = &$temp[$employee['number']]; } else { // 如果经理不在数组中(比如顶层Adam的经理57900不在列表里),就把当前员工作为顶层节点加入结果 if ($employee['level'] === 0) { $result[] = &$temp[$employee['number']]; } } } // 现在 $result 就是你要的多层级嵌套结构了 print_r($result);
为什么这个方法能生效?
通过引用存储每个员工,当我们把底层员工(比如Veronica)挂载到Larry的children里时,Larry本身已经是Edward的子节点,Edward又是Adam的子节点——所有层级的修改都是联动的,最终自然形成完整的树形结构。
方法二:递归法(简洁直观)
如果你的员工层级不多,递归法的代码更简洁易懂,逻辑也更直观:
/** * 递归构建层级结构 * @param array $employees 所有员工数组 * @param string|int $managerNumber 要查找下属的经理编号 * @return array 下属的层级结构 */ function buildEmployeeHierarchy($employees, $managerNumber) { $children = []; foreach ($employees as $employee) { // 如果当前员工的直属经理是传入的编号,就递归处理它的下属 if ($employee['line_manager_number'] === $managerNumber) { $employee['children'] = buildEmployeeHierarchy($employees, $employee['number']); $children[] = $employee; } } return $children; } // 第一步:先找出所有顶层节点(level=0) $topLevelEmployees = array_filter($holding, function($emp) { return $emp['level'] === 0; }); // 第二步:为每个顶层节点构建完整的下属层级 $result = []; foreach ($topLevelEmployees as $topEmp) { $topEmp['children'] = buildEmployeeHierarchy($holding, $topEmp['number']); $result[] = $topEmp; } print_r($result);
两种方法对比
- 迭代法:效率更高,仅遍历数组两次,适合处理大量员工数据,不会有栈溢出风险。
- 递归法:代码更简洁,逻辑容易理解,但如果层级过深(比如超过100层),可能会触发PHP的递归栈溢出错误。
你原来代码的问题
你之前的代码只把当前员工直接加到$holding[$v["line_manager_number"]]["children"]里,但这里修改的是原数组中经理节点的副本,而不是已经被挂载到上级节点里的那个经理对象。所以只有第一层下属会被添加,深层的嵌套关系无法建立——用引用就能解决这个核心问题。
内容的提问来源于stack exchange,提问作者pee2pee
相关产品推荐
相关产品推荐

