PHP:将嵌套层级数据结构扁平化为连续数组的实现教程

PHP:将嵌套层级数据结构扁平化为连续数组的实现教程

本教程详细介绍了如何将php中复杂的嵌套对象或数组(通常用于表示树形结构,如商品分类)转换为一个扁平化的连续数组。通过结合对象到数组的转换函数和递归遍历算法,我们将演示如何高效地提取树形结构中的每个节点数据,并将其组织成一个易于处理的列表,同时移除原始结构中的子节点信息,以满足特定数据处理或展示需求。

在许多Web应用中,我们经常需要处理具有层级关系的数据,例如网站导航菜单、商品分类或组织架构。这些数据通常以嵌套的对象或数组形式存储,其中每个节点可能包含一个 children 属性来指向其子节点。然而,在某些场景下,我们需要将这种树形结构扁平化为一个简单的、不含嵌套的连续数组,以便于列表展示、数据导出或简化后续处理逻辑。

场景描述与需求分析

假设我们有一个 Categories_store_tree 对象,它内部包含一个名为 list_of_sections 的私有属性,该属性存储着一个表示商品分类的树形结构。原始数据结构如下所示:

object(Categories_store_tree)#519 (1) {      ["list_of_sections":"Categories_store_tree":private]=> Array(5) {                           ["id"]=> int(1)          ["name"]=> string(11) "Main Store"          ["parent_id"]=> NULL          ["children"]=> array(2) {              [0]=> array(5) {                  ["id"]=> int(2)                  ["name"]=> string(4) "Food"                  ["parent_id"]=> int(1)                  ["children"]=> array(0) { }              }              [1]=> array(5) {                  ["id"]=> int(3)                  ["name"]=> string(14) "Electronics"                  ["parent_id"]=> int(1)                  ["children"]=> array(2) {                      [0]=> array(5) {                          ["id"]=> int(4)                          ["name"]=> string(8) "Headphones"                          ["parent_id"]=> int(3)                          ["children"]=> array(0) { }                      }                      [1]=> array(5) {                          ["id"]=> int(5)                          ["name"]=> string(5) "Smartphones"                          ["parent_id"]=> int(3)                          ["children"]=> array(0) { }                      }                  }              }          }      }  } 

我们的目标是将其转换为一个扁平化的数组结构,其中每个元素代表一个分类,且不包含 children 属性,如下所示:

object(Categories_store_tree)#964 (1) {      ["list_of_sections":"Categories_store_tree":private]=> array(5) {          [0]=> array(4) {              ["id"]=> int(1)              ["name"]=> string(11) "Main Store"              ["parent_id"]=> NULL          }          [1]=> array(4) {              ["id"]=> int(2)              ["name"]=> string(4) "Food"              ["parent_id"]=> int(1)          }          [2]=> array(4) {              ["id"]=> int(3)              ["name"]=> string(14) "Electronics"              ["parent_id"]=> int(1)          }          [3]=> array(4) {              ["id"]=> int(4)              ["name"]=> string(8) "Headphones"              ["parent_id"]=> int(3)          }          [4]=> array(4) {              ["id"]=> int(5) 马             ["name"]=> string(5) "Smartphones"              ["parent_id"]=> int(3)          }      }  }

注意,目标结构中 list_of_sections 的值现在是一个索引数组,包含了所有分类的扁平列表。

立即学习PHP免费学习笔记(深入)”;

实现步骤与代码示例

为了实现上述转换,我们需要两个主要步骤:

  1. 将初始对象转换为多维数组:这有助于我们统一数据结构,便于后续处理。
  2. 递归遍历并扁平化数组:这是核心步骤,通过递归函数遍历树形结构,提取每个节点的关键信息,并将其添加到扁平化列表中。

1. 对象到数组的转换

如果您的原始数据是一个php对象,首先需要将其转换为一个多维数组。这可以通过一个通用的递归函数实现。

PHP:将嵌套层级数据结构扁平化为连续数组的实现教程

即构数智人

即构数智人是由即构科技推出的AI虚拟数字人视频创作平台,支持数字人形象定制、短视频创作、数字人直播等。

PHP:将嵌套层级数据结构扁平化为连续数组的实现教程36

查看详情 PHP:将嵌套层级数据结构扁平化为连续数组的实现教程

<?php  /**  * 递归地将对象转换为数组。  * 如果输入是对象,则获取其所有属性并递归转换;  * 如果输入是数组,则递归转换其所有元素;  * 否则,直接返回输入值。  *  * @param mixed $d 待转换的对象或数组。  * @return array|mixed 转换后的数组或原始值。  */ function objectToArray($d) {     if (is_object($d)) {         // 获取对象的公共属性         $d = get_object_vars($d);     }     // 如果是数组,则递归处理每个元素     return is_array($d) ? array_map(__METHOD__, $d) : $d; }  // 假设 $originalObject 是您的 Categories_store_tree 实例 // 例如,为了演示,我们模拟一个这样的对象 class Categories_store_tree {     private $list_of_sections;      public function __construct(array $data) {         $this->list_of_sections = $data;     } }  $initialData = [     "id" => 1,     "name" => "Main Store",     "parent_id" => NULL,     "children" => [         [             "id" => 2,             "name" => "Food",             "parent_id" => 1,             "children" => []         ],         [             "id" => 3,             "name" => "Electronics",             "parent_id" => 1,             "children" => [                 [                     "id" => 4,                     "name" => "Headphones",                     "parent_id" => 3,                     "children" => []                 ],                 [                     "id" => 5,                     "name" => "Smartphones",                     "parent_id" => 3,                     "children" => []                 ]             ]         ]     ] ];  $originalObject = new Categories_store_tree($initialData);  // 将对象转换为数组 $convertedArray = objectToArray($originalObject);  echo "--- 转换后的原始数组结构 ---" . PHP_EOL; print_r($convertedArray); echo PHP_EOL;

经过 objectToArray 函数处理后,$convertedArray 将是一个纯粹的PHP数组,其结构与原始对象的内部结构保持一致,只是不再是对象形式。

2. 递归遍历与扁平化

接下来,我们需要编写一个递归函数来遍历这个多维数组。这个函数将访问每个节点,提取我们需要的 id, name, parent_id 属性,并将其添加到一个新的扁平化列表中。

<?php  // 承接上文的 objectToArray 函数和 $convertedArray 变量  /**  * 递归地扁平化分类树结构。  * 遍历每个节点,提取其核心信息,并添加到扁平化列表中。  *  * @param array $node 当前处理的节点数组。  * @param array $flatList 引用传递的扁平化列表,用于收集所有分类。  */ function flattenCategoryTree(array $node, array &$flatList) {     // 提取当前节点的核心信息     $currentCategory = [         'id' => $node['id'],         'name' => $node['name'],         'parent_id' => $node['parent_id']     ];     $flatList[] = $currentCategory; // 将当前分类添加到扁平化列表      // 检查是否存在子节点,如果存在且为非空数组,则递归处理     if (isset($node['children']) && is_array($node['children']) && !empty($node['children'])) {         foreach ($node['children'] as $childNode) {             flattenCategoryTree($childNode, $flatList);         }     } }  // 初始化一个空数组来存储扁平化后的分类列表 $flatCategories = [];  // 假设 $convertedArray['list_of_sections'] 是我们分类树的根节点 // 确保 'list_of_sections' 存在且是一个数组 if (isset($convertedArray['list_of_sections']) && is_array($convertedArray['list_of_sections'])) {     flattenCategoryTree($convertedArray['list_of_sections'], $flatCategories); }  echo "--- 扁平化后的分类列表 ---" . PHP_EOL; print_r($flatCategories); echo PHP_EOL;  // 如果需要将其重新封装到 Categories_store_tree 对象中(如示例输出所示) // 尽管通常扁平化后就不需要再封装回原对象,但为了匹配示例,可以这样做 $finalObject = new Categories_store_tree($flatCategories); echo "--- 重新封装到对象后的结构 (与目标输出格式匹配) ---" . PHP_EOL; print_r($finalObject); echo PHP_EOL;  ?>

完整代码示例

将上述两个部分整合,形成一个完整的解决方案:

<?php  /**  * 递归地将对象转换为数组。  * 如果输入是对象,则获取其所有属性并递归转换;  * 如果输入是数组,则递归转换其所有元素;  * 否则,直接返回输入值。  *  * @param mixed $d 待转换的对象或数组。  * @return array|mixed 转换后的数组或原始值。  */ function objectToArray($d) {     if (is_object($d)) {         // 获取对象的公共属性。对于私有属性,get_object_vars 默认无法访问。         // 如果需要访问私有属性,可能需要通过反射API或在类内部提供公共方法。         // 在本例中,我们假设 Categories_store_tree 的 list_of_sections 是可访问的,         // 或者通过某种方式(如魔术方法 __get)使其在对象转换时被捕获。         // 实际上,如果属性是 private,get_object_vars 将不会直接返回它。         // 为了匹配原始问题中能访问到 'list_of_sections' 的场景,         // 我们需要一个更健壮的 objectToArray,或者假设 'list_of_sections' 是 public 或通过其他方式可访问。         // 鉴于原始问题中的 var_dump 格式,我们假设 objectToArray 能够处理。         $d = (array) $d; // 强制类型转换为数组可以暴露protected/private属性,但键名会改变     }     return is_array($d) ? array_map(__METHOD__, $d) : $d; }  /**  * 递归地扁平化分类树结构。  * 遍历每个节点,提取其核心信息,并添加到扁平化列表中。  *  * @param array $node 当前处理的节点数组。  * @param array $flatList 引用传递的扁平化列表,用于收集所有分类。  */ function flattenCategoryTree(array $node, array &$flatList) {     // 提取当前节点的核心信息     $currentCategory = [         'id' => $node['id'],         'name' => $node['name'],         'parent_id' => $node['parent_id']     ];     $flatList[] = $currentCategory; // 将当前分类添加到扁平化列表      // 检查是否存在子节点,如果存在且为非空数组,则递归处理     if (isset($node['children']) && is_array($node['children']) && !empty($node['children'])) {         foreach ($node['children'] as $childNode) {             flattenCategoryTree($childNode, $flatList);         }     } }  // 模拟 Categories_store_tree 类和其数据 class Categories_store_tree {     // 在实际应用中,如果 list_of_sections 是 private,     // objectToArray 可能需要特殊处理或通过反射才能访问。     // 为了简化和匹配 var_dump 行为,这里将其设为 public 或提供访问器。     // 在本例中,我们直接模拟一个可访问的结构。     public $list_of_sections;      public function __construct($data) {         $this->list_of_sections = $data;     } }  // 原始嵌套数据结构 $initialNestedData = [     "id" => 1,     "name" => "Main Store",     "parent_id" => NULL,     "children" => [         [             "id" => 2,             "name" => "Food",             "parent_id" => 1,             "children" => []         ],         [             "id" => 3,             "name" => "Electronics",             "parent_id" => 1,             "children" => [                 [                     "id" => 4,                     "name" => "Headphones",                     "parent_id" => 3,                     "children" => []                 ],                 [                     "id" => 5,                     "name" => "Smartphones",                     "parent_id" => 3,                     "children" => []                 ]             ]         ]     ] ];  // 实例化模拟的 Categories_store_tree 对象 $originalObject = new Categories_store_tree($initialNestedData);  echo "--- 原始对象结构 (模拟) ---" . PHP_EOL; var_dump($originalObject); echo PHP_EOL;  // 1. 将对象转换为数组 // 注意:如果 `list_of_sections` 是 private,直接 `(array) $originalObject` 会导致键名变化 // 更好的做法是: $convertedArray = ['list_of_sections' => objectToArray($originalObject->list_of_sections)]; // 或者如果 objectToArray 能处理私有属性,直接 $convertedArray = objectToArray($originalObject); // 这里我们假设 objectToArray 能够正确处理,或者我们直接从对象中取出需要处理的部分。 // 鉴于原始问题给出的 objectToArray 函数,它通常用于 stdClass 或公共属性。 // 对于 private 属性,更准确的做法是: $reflectionClass = new ReflectionClass($originalObject); $property = $reflectionClass->getProperty('list_of_sections'); $property->setaccessible(true); // 使私有属性可访问 $convertedArrayRoot = $property->getValue($originalObject); $convertedArray = ['list_of_sections' => objectToArray($convertedArrayRoot)]; // 再次确保子结构也被转换  echo "--- 转换后的中间数组结构 ---" . PHP_EOL; print_r($convertedArray); echo PHP_EOL;  // 2. 初始化一个空数组来存储扁平化后的分类列表 $flatCategories = [];  // 确保 'list_of_sections' 存在且是一个数组,然后开始扁平化 if (isset($convertedArray['list_of_sections']) && is_array($convertedArray['list_of_sections'])) {     flattenCategoryTree($convertedArray['list_of_sections'], $flatCategories); }  echo "--- 扁平化后的分类列表 ---" . PHP_EOL; print_r($flatCategories); echo PHP_EOL;  // 如果需要将扁平化结果重新封装到 Categories_store_tree 对象中,以匹配目标输出格式 $finalObject = new Categories_store_tree($flatCategories);  echo "--- 重新封装到 Categories_store_tree 对象后的结构 ---" . PHP_EOL; var_dump($finalObject); echo PHP_EOL;  ?>

注意事项与总结

  1. 对象属性访问: 如果 Categories_store_tree 类中的 list_of_sections 属性是 private 或 protected,直接使用 get_object_vars() 或 (array) 强制类型转换可能无法直接访问到它,或者会导致键名发生变化(如 Categories_store_treelist_of_sections)。在实际应用中,您可能需要:
    • 在 Categories_store_tree 类中提供一个公共的 getter 方法来获取 list_of_sections。
    • 使用 PHP 的反射(ReflectionClass)API 来访问私有或保护属性,如完整代码示例中所示。
  2. 数据结构一致性: 确保您的 children 键名在整个树结构中保持一致。如果存在其他名称的子节点键,您需要调整 flattenCategoryTree 函数中的 isset($node[‘children’]) 条件。
  3. 性能考虑: 对于非常深的树结构或包含大量节点的树,递归函数可能会消耗较多的内存(因为每次递归调用都会增加调用深度)。在极端情况下,可能会遇到溢出错误。对于此类情况,可以考虑使用迭代方式(例如,使用栈或队列)来实现扁平化,但这会增加代码的复杂性。
  4. 错误处理: 在实际生产环境中,您可能需要添加更多的错误检查,例如 id、name、parent_id 键是否存在,以防止因数据不完整而导致的运行时错误。

通过上述方法,您可以有效地将复杂的嵌套树形数据结构转换为扁平化的列表,这在许多数据展示和处理场景中都

以上就是PHP:将嵌套层级数据结构扁平化为连续数组的实现教程的详细内容,更多请关注

上一篇
下一篇
text=ZqhQzanResources