在PHP开发中,处理具有层级关系的数据时,经常会遇到扁平数组需要转换为深层嵌套结构的需求,比如分类列表、组织架构数据等,这类数据通常通过一个父级标识字段来关联上下级节点。

核心转换思路
扁平数组转换为嵌套结构的核心逻辑是:先遍历所有扁平节点,为每个节点添加子节点容器,再通过父级标识将子节点挂载到对应的父节点下。如果是无限层级结构,还可以通过递归的方式处理更深层的嵌套关系。
基础示例数据
假设我们有以下扁平的分类数组,每个元素包含id、name和parent_id三个字段,parent_id为0表示顶级分类:
$flat_array = [
['id' => 1, 'name' => '电子产品', 'parent_id' => 0],
['id' => 2, 'name' => '手机', 'parent_id' => 1],
['id' => 3, 'name' => '电脑', 'parent_id' => 1],
['id' => 4, 'name' => '智能手机', 'parent_id' => 2],
['id' => 5, 'name' => '笔记本电脑', 'parent_id' => 3],
['id' => 6, 'name' => '服装', 'parent_id' => 0],
['id' => 7, 'name' => '男装', 'parent_id' => 6],
];方法一:引用赋值实现转换
通过遍历两次数组实现转换,第一次遍历初始化所有节点的子节点容器并建立id到节点的映射,第二次遍历根据parent_id将子节点挂载到父节点。
function flat_to_nested($flat_list) {
$result = [];
$node_map = [];
// 第一次遍历:初始化每个节点的children字段,同时建立id到节点的映射
foreach ($flat_list as &$item) {
$item['children'] = [];
$node_map[$item['id']] = &$item;
}
unset($item); // 断开引用,避免后续遍历出现问题
// 第二次遍历:将子节点挂载到对应的父节点下
foreach ($node_map as &$node) {
$parent_id = $node['parent_id'];
if ($parent_id == 0) {
// 顶级节点直接加入结果数组
$result[] = &$node;
} else {
// 非顶级节点挂载到父节点的children中
if (isset($node_map[$parent_id])) {
$node_map[$parent_id]['children'][] = &$node;
}
}
}
unset($node);
return $result;
}
$nested_array = flat_to_nested($flat_array);
print_r($nested_array);代码逻辑说明
- 使用引用赋值
&确保每个节点的修改会同步到映射表和最终结果中 - 第一次遍历为每个节点添加空的
children数组,避免后续挂载子节点时出现未定义索引错误 - 第二次遍历通过
parent_id判断节点归属,顶级节点直接放入结果集,子节点挂载到父节点的children数组中
方法二:递归实现无限层级转换
如果层级深度不确定,也可以使用递归的方式实现转换,通过指定父级ID查找所有对应的子节点,再递归处理子节点的子节点。
function build_tree($flat_list, $parent_id = 0) {
$tree = [];
foreach ($flat_list as $item) {
if ($item['parent_id'] == $parent_id) {
// 查找当前节点的子节点,递归处理
$children = build_tree($flat_list, $item['id']);
if (!empty($children)) {
$item['children'] = $children;
} else {
$item['children'] = [];
}
$tree[] = $item;
}
}
return $tree;
}
$recursive_nested = build_tree($flat_array);
print_r($recursive_nested);两种方法的对比
| 对比项 | 引用赋值法 | 递归法 |
|---|---|---|
| 时间复杂度 | O(n),两次遍历即可完成 | O(n * 平均层级深度),多次遍历数组 |
| 适用场景 | 数据量较大、层级明确的场景 | 层级深度不确定、数据量较小的场景 |
| 实现难度 | 需要理解引用赋值,逻辑稍复杂 | 逻辑直观,容易理解 |
边界情况处理
实际使用中还需要处理一些边界情况,避免转换出错:
- 如果扁平数组中存在无效的
parent_id(即父节点不存在),可以选择忽略该节点或者单独放到一个未分类的分组中 - 如果数组中存在重复的
id,需要在初始化映射表时做去重处理,避免覆盖已有节点 - 如果
parent_id字段名称不固定,可以将字段名作为参数传入函数,提高方法通用性
注意:使用引用赋值时,遍历结束后要及时断开引用,避免后续代码修改数组时出现意外的数据变更。
PHP数组嵌套扁平数组转换array_reduce修改时间:2026-06-06 06:12:03