PHP分层数组 - 父母和孩子

Posted

技术标签:

【中文标题】PHP分层数组 - 父母和孩子【英文标题】:PHP hierarchical array - Parents and childs 【发布时间】:2012-12-02 09:27:33 【问题描述】:

我将 phpmysql 与 Idiorm 一起使用。这可能不相关。

我的 PHP 数组

这是父母与孩子之间的关系。 0 是根父级。 示例:根父 0 拥有子 33,子 33 拥有子 27 孩子 71.

如果需要解决问题,可以更改此数组结构。

array (
  33 => 
    array (
      0 => '27',
      1 => '41',
  ),
  27 => 
    array (
      0 => '64',
      1 => '71',
  ),
  0 => 
    array (
      0 => '28',
      1 => '29',
      2 => '33',
  ),
)

我的分层结果

类似这样的东西,但是作为一个数组...

  0 => 
      28
      29
      33
         27 =>
               64
               71
         41

信息

深度未知,可以无限。我尝试了 foreach,但可能不是这样。

我自己的想法

一些递归函数? 一些while循环?

我尝试了以上两种方法,但结果一团糟。这是一个脑筋急转弯。

【问题讨论】:

请参阅***.com/a/8587437/476 以获得正确方向的推动... 【参考方案1】:

我遇到了另一个问题,在此页面上找不到适合我的解决方案。我需要创建一棵树,但 不知道根 ID。 这意味着我必须遍历我的平面数组并在树的顶部构建具有最父项的分支。

如果其他人需要构建没有根父项 ID 的树,我就是这样做的。

<?php

$rows = [
    (object) [
        'id' => 1001,
        'parentid' => 1000,
        'name' => 'test1.1'
    ],
    (object) [
        'id' => 1000,
        'parentid' => 100,
        'name' => 'test1'
    ],
    (object) [
        'id' => 1002,
        'parentid' => 1000,
        'name' => 'test1.2'
    ],
    (object) [
        'id' => 1004,
        'parentid' => 1001,
        'name' => 'test1.1.1'
    ],
    (object) [
        'id' => 1005,
        'parentid' => 1004,
        'name' => 'test1.1.1.1'
    ],
    (object) [
        'id' => 100,
        'parentid' => 10,
        'name' => 'test 0'
    ],
    (object) [
        'id' => 1006,
        'parentid' => 1002,
        'name' => 'test1.2.1'
    ],
    (object) [
        'id' => 1007,
        'parentid' => 1002,
        'name' => 'test1.2.2'
    ],
];

function add_child(stdClass $parent, stdClass $child) 
    if ($child->parentid != $parent->id) 
        throw new Exception('Attempting to add child to wrong parent');
    

    if (empty($parent->children)) 
        $parent->children = [];
     else 
        // Deal where already in branch.
        foreach ($parent->children as $idx => $chd) 
            if ($chd->id === $child->id) 
                if (empty($chd->children)) 
                    // Go with $child, since $chd has no children.
                    $parent->children[$idx] = $child;
                    return;
                 else 
                    if (empty($child->children)) 
                        // Already has this child with children.
                        // Nothing to do.
                        return;
                     else 
                        // Both childs have children - merge them.
                        $chd->children += $child->children;
                        $parent->children[$idx] = $child;
                        return;
                    
                
            
        
    

    $parent->children[] = $child;


function build_branch(&$branch, &$rows, &$parent = null) 
    $hitbottom = false;
    while (!$hitbottom) 
        $foundsomething = false;
        // Pass 1 - find children.
        $removals = []; // Indexes of rows to remove after this loop.
        foreach ($rows as $idx => $row) 
            if ($row->parentid === $branch->id) 
                // Found a child.
                $foundsomething = true;
                // Recurse - find children of this child.
                build_branch($row, $rows, $branch);
                add_child($branch, $row);
                $removals[] = $idx;
            
        
        foreach ($removals as $idx) 
            unset($rows[$idx]);
        
        // Pass 2 - find parents.
        if ($parent === null) 
            $foundparent = false;
            foreach ($rows as $idx => $row) 
                if ($row->id === $branch->parentid) 
                    // Found parent
                    $foundsomething = true;
                    $foundparent = true;

                    add_child($row, $branch);
                    unset ($rows[$idx]);
                    // Now the branch needs to become the parent since parent contains branch.
                    $branch = $row;
                    // No need to search for other parents of this branch.
                    break;
                
            
        

        $hitbottom = !$foundsomething;
    


function build_tree(array $rows) 
    $tree = [];
    while (!empty($rows)) 
        $row = array_shift($rows);
        build_branch($row, $rows);
        $tree[] = $row;
    
    return $tree;


$tree = build_tree($rows);

print_r($tree);

【讨论】:

有趣的一个。你的问题是任何人都可以成为根?我通常认为根是带有parentId = null 的根,但可能不是你的情况。【参考方案2】:
public function createTree (&$list, $parentId = null) 
    $tree = array();
    foreach ($list as $key => $eachNode) 
        if ($eachNode['parentId'] == $parentId) 
            $eachNode['children'] = $this->createTree ($list,$eachNode['id']);
            $tree[] = $eachNode;
            unset($list[$key]);
        
    
    return $tree;

在该函数中传递关联数组,如果最父不为空,则只需将最父 id 作为第二个参数传递。

【讨论】:

【参考方案3】:

来自@Jens Törnell 的好答案,只是想添加一点改进,如果您的 parent_id 和 id 实际上是字符串而不是数字,那么上述方法将失败,在创建子数组后,它将再次创建这些子数组作为单独的个体大批。为了解决这个问题,您应该进行三重相等检查并通过比较变量的数据类型,即(字符串)。

数组中基于字符串的Id和Parent_id

function buildTree(array $elements, $parentId = 0) 
    $branch = array();

    foreach ($elements as $element) 
        if ((string)$element['parent_id']  === (string)$parentId) 
            $children = buildTree($elements, $element['id']);
            if ($children) 
                $element['children'] = $children;
            
            $branch[] = $element;
        
    

    return $branch;

此外,如果有人愿意,他也可以添加第三个参数来动态指定变量的数据类型,即function buildTree(array $elements, $parentId = 0, $datatype='string'),但您将不得不承担任何其他错误的发生。

希望对某人有所帮助!

【讨论】:

这里不工作。当添加数据类型时,我得到的结果是空的。但是当我删除数据类型时,结果显示在那里。【参考方案4】:

我添加到@Jens Törnell 的答案中,以便能够定义 parent_id 的列名、子数组键名以及 id 的列名的选项。

/**
 * function buildTree
 * @param array $elements
 * @param array $options['parent_id_column_name', 'children_key_name', 'id_column_name'] 
 * @param int $parentId
 * @return array
 */
function buildTree(array $elements, $options = [
    'parent_id_column_name' => 'parent_id',
    'children_key_name' => 'children',
    'id_column_name' => 'id'], $parentId = 0)
    
    $branch = array();
    foreach ($elements as $element) 
        if ($element[$options['parent_id_column_name']] == $parentId) 
            $children = buildTree($elements, $options, $element[$options['id_column_name']]);
            if ($children) 
                $element[$options['children_key_name']] = $children;
            
            $branch[] = $element;
        
    
    return $branch;

由于功能非常通用,我在大部分项目中都设法使用了上述功能。

【讨论】:

【参考方案5】:

@deceze 的建议奏效了。但是输入数组需要改变一点,像这样......

$rows = array(
    array(
        'id' => 33,
        'parent_id' => 0,
    ),
    array(
        'id' => 34,
        'parent_id' => 0,
    ),
    array(
        'id' => 27,
        'parent_id' => 33,
    ),
    array(
        'id' => 17,
        'parent_id' => 27,
    ),
);

来自https://***.com/a/8587437/476:

function buildTree(array $elements, $parentId = 0) 
    $branch = array();

    foreach ($elements as $element) 
        if ($element['parent_id'] == $parentId) 
            $children = buildTree($elements, $element['id']);
            if ($children) 
                $element['children'] = $children;
            
            $branch[] = $element;
        
    

    return $branch;


$tree = buildTree($rows);

print_r( $tree );

【讨论】:

刚刚在我的回答中添加了一点改进,希望没问题! 如果您不想在具有 parent_id 结构的表上运行多个查询或左连接甚至联合,这是一个很好的解决方案!

以上是关于PHP分层数组 - 父母和孩子的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PDO 从 PHP 中的单个 select 语句将孩子分配给父母?

PHP 任何孩子的绝对父母

php 显示当前菜单父母当前页面的孩子/兄弟姐妹

从平面列表构建分层列表

无法正确地将值从父母传递给孩子和孩子传递给父母

选中/取消选中父母和孩子