51

我环顾了互联网,并没有完全找到我要找的东西。我有一个平面数组,每个元素都包含一个“id”和一个“parent_id”。每个元素只有一个父元素,但可能有多个子元素。如果 parent_id = 0,则将其视为根级别项。我正试图将我的平面阵列变成一棵树。我发现的其他示例仅将元素复制到父元素,但原始元素仍然存在。

编辑

起始数组的每个元素都是从一个单独的 XML 文件中读取的。如果文件没有父级,则文件本身将具有“0”作为 parent_id 的值。键实际上是字符串。

我很抱歉之前的混乱。希望这更清楚:

/编辑

我的起始数组:

大批
(
    [_319_] => 数组
        (
            [id] => 0
            [parent_id] => 0
        )

    [_320_] => 数组
        (
            [id] => _320_
            [parent_id] => 0
        )

    [_321_] => 数组
        (
            [id] => _321_
            [parent_id] => _320_
        )

    [_322_] => 数组
        (
            [id] => _322_
            [parent_id] => _321_
        )

    [_323_] => 数组
        (
            [id] => _323_
            [parent_id] => 0
        )

    [_324_] => 数组
        (
            [id] => _324_
            [parent_id] => _323_
        )

    [_325_] => 数组
        (
            [id] => _325_
            [parent_id] => _320_
        )
)

生成树后的结果数组:

大批
(
    [_319_] => 数组
        (
            [id] => _319_
            [parent_id] => 0
        )

    [_320_] => 数组
        (
            [id] => _320_
            [parent_id] => 0
            [孩子] => 数组
                (
                    [_321_] => 数组
                        (
                            [id] => _321_
                            [parent_id] => _320_
                            [孩子] => 数组
                                (
                                    [_322_] => 数组
                                        (
                                            [id] => _322_
                                            [parent_id] => _321_
                                        )
                                )
                        )
                    [_325_] => 数组
                        (
                            [id] => _325_
                            [parent_id] => _320_
                        )
                )
    [_323_] => 数组
        (
            [id] => _323_
            [parent_id] => 0
            [孩子] => 数组
                (
                    [_324_] => 数组
                        (
                            [id] => _324_
                            [parent_id] => _323_
                        )
                )
        )

非常感谢任何帮助/指导!

到目前为止我有一些代码:

        函数 buildTree(数组 &$elements, $parentId = 0) {
        $分支 = 数组();

        foreach ($elements 作为 $element) {
            if ($element['parent_id'] == $parentId) {
                $children = $this->buildTree($elements, $element['id']);
                如果($儿童){
                    $element['children'] = $children;
                }
                $分支[] = $元素;
            }
        }

        返回$分支;
    }

4

13 回答 13

68

你忘了unset()那里的兄弟。

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['id']] = $element;
            unset($elements[$element['id']]);
        }
    }
    return $branch;
}
于 2012-01-12T20:36:02.960 回答
37

ImmortalFirefly 的解决方案是有效的,然而,正如 mrded 指出的那样,它并不能拯救没有孩子的第一任父母。我已经编辑了函数来解决这个问题:

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['id']] = $element;
            unset($element);
        }
    }
    return $branch;
}
于 2015-02-10T10:34:25.603 回答
6

这对我有用:

$index=array();
$tree=array();
foreach ($ori as $key=>$var) {
  $var=array_shift($ori);
  if ($var['id']==0) $var['id']=$key;
  if ((string)$var['parent_id']==='0') {
    $tree[$key]=$var;
    $index[$key]=&$tree[$key];
  } else if (isset($index[$var['parent_id']])) {
    if (!isset($index[$var['parent_id']]['children'])) $index[$var['parent_id']]['children']=array();
    $index[$var['parent_id']]['children'][$key]=$var;
    $index[$key]=&$index[$var['parent_id']]['children'][$key];
  } else {
    array_push($ori,$var);
  }
}
unset($index);
print_r($tree);
于 2012-01-12T19:22:04.123 回答
4

我可以看到逻辑,将其保存在结果中:

Array
(
    [0] => Array
        (
            [id] => 0
            [parent_id] => 0
        )

    [1] => Array
        (
            [id] => 1
            [parent_id] => 0
        )

恕我直言,parent_id = o,[1] 不应该是 [0] 的孩子吗?

无论如何,参考救援:

$tree = array();
foreach($inputarray as $item){
     if(!isset($tree[$item['id']])) $tree[$item['id']] = array();
     $tree[$item['id']] = array_merge($tree[$item['id']],$item);
     if(!isset($tree[$item['parent_id']])) $tree[$item['parent_id']] = array();
     if(!isset($tree[$item['parent_id']]['children'])) $tree[$item['parent_id']]['children'] = array();
     $tree[$item['parent_id']]['children'][] = &$tree[$item['id']];
}
$result = $tree[0]['children'];
unset($tree);
print_r($result);

因为您滥用了 0 作为 root 的“神奇”数字和现有的 id,所以我们现在在 id=0 分支中进行了递归。添加if($item['parent_id']!=$item['id'])之前$tree[$item['parent_id']]['children'][] = &$tree[$item['id']];可以防止这种情况,但它并不漂亮。

于 2012-01-12T18:38:04.880 回答
3

可以使用此函数(parent_id,id,title)构建稍微不同的源数组:

$q = mysql_query("SELECT id, parent_id, name FROM categories");
while ($r = mysql_fetch_row($q)) {
  $names[$r[0]] = $r[2];
  $children[$r[0]][] = $r[1];
 }

function render_select($root=0, $level=-1) {
  global $names, $children;
  if ($root != 0)
    echo '<option>' . strrep(' ', $level) . $names[$root] . '</option>';
  foreach ($children[$root] as $child)
    render_select($child, $level+1);
}

echo '<select>';
render_select();
echo '</select>';
  1. 更高效的层级系统
于 2012-01-12T18:40:12.943 回答
3

虽然这是一个老问题,但我将在这里发布我的答案:

/* assuming top level pid = 0 */
$rows = array (
    array ( 'id' => 1, 'pid' => 0 ),
    /* ... */
);

/* make id become array key */
$rows = array_column ( $rows, null, 'id' ); 

foreach ( $rows as $key => $val ) {
    if ( $val ['pid'] ) {
        if ( isset ( $rows [$val ['pid']] )) {
            $rows [$val ['pid']]['children'][] = &$rows [$key];
        }
    }
}

foreach ( $rows as $key => $val ) {
    if ( $val ['pid'] ) unset ( $rows [$key] );
}

array_column是 PHP 5.5,但您可以轻松制作自己的。

于 2014-08-25T03:02:03.247 回答
1

您想查看在 MySQL 中存储和加载分层数据,因为这应该可以解决一些问题。我假设第一个数组代表直接从数据库中获取的数据?

看起来您正在尝试使用邻接模型将数据组织到层次结构中。还有其他方法可以使用嵌套来实现这一点。如果您不从数据库中获取这些数据,那么这可能没有那么有用。

此链接应该可以帮助您: http: //mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

于 2012-01-12T18:48:20.283 回答
1

SteveEdson 的代码工作正常,除非元素的父元素在原始数据结构中不存在。这是我的解决方法(但是,它从元素中删除了“parent_id”,这可能会也可能不会接受):

function buildTree(array &$elements, $parentId = 0)
{
    $branch = array();
    foreach ($elements as &$element) {
        if ($element["parent_id"] != null && $elements[$element["parent_id"]] == null)
            unset($element["parent_id"]);        
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[$element['id']] = $element;
            unset($element);
        }
    }
    return $branch;
}
于 2018-11-29T19:51:24.443 回答
0

这是我的解决方案,如果我们假设顶级 parent_id = 0,则效果理想:

function MakeTree($arr){
    $parents_arr=array();
    foreach ($arr as $key => $value) {
        $parents_arr[$value['pid']][$value['id']]=$value;
    }
    $tree=$parents_arr['0'];
    $this->createTree($tree, $parents_arr);
    return $tree;
}
function createTree(&$tree, $parents_arr){
    foreach ($tree as $key => $value) {
        if(!isset($value['children'])) {
            $tree[$key]['children']=array();
        }
        if(array_key_exists($key, $parents_arr)){
            $tree[$key]['children']=$parents_arr[$key];
            $this->createTree($tree[$key]['children'], $parents_arr);
        }
    }
}
于 2013-04-13T11:53:59.040 回答
0

这是我的解决方案,复制和优化其他解决方案。

function buildTree(array &$elements, $parentId = 0) {
    $branch = array();
    foreach ($elements as $key => $element) {
        if ($element['parent_id'] == $parentId) {
            $children = $this->buildTree($elements, $key);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[$key] = $element;
            unset($elements[$key]);
        }
    }
    return $branch;
}
于 2015-02-27T16:13:03.340 回答
0

清洁、短且无镇流器。要树的数组数组:

class Mother {
    private $root;
    public function treeInit($array)
    {
        $this->root = new Child();
        foreach($array as $value){
            $this->root->treeClimb(array_reverse($value));
        }
        return $this->root;
    }
}

class Child {
    private $children = [];
    public function treeClimb($arr)
    {
        if(count($arr) > 0) {
            $childTmp = array_pop($arr);
            if(!key_exists($childTmp,$this->children))
            {
                $this->children[$childTmp] = new Child();
            }
        $this->children[$childTmp]->treeClimb($arr);
        }
    }
}

$array = array(array('obst','banae','krumm','gelb'),
                    array('obst','beere','him'),
                    array('obst','beere','brom'),
                    array('obst','banae','gerade'),
                    array('veg','carot','gerade'));

$obj = new Mother();
var_dump($obj->treeInit($array));
于 2018-01-21T13:43:49.280 回答
0

我想出了一个与@eugen-rieck 类似的解决方案并想分享它。不过,我命名$branches了我的索引数组。

$tree = [];
$branches = [];

while (!empty($input)) {
    $beforeCount = count($input);

    foreach ($input as $id => $item) {
        $pid = $item['parent_id'];

        if (isset($branches[$pid])) {
            $branches[$pid]['children'][$id] = $item;
            $branches[$id] = &$branches[$pid]['children'][$id];
            unset($input[$id]);
        }
    }

    if ($beforeCount === count($input)) {
        $firstItem = array_shift($input);
        $id = $firstItem['id'];
        $tree[$id] = $firstItem;
        $branches[$id] = &$tree[$id];
    }
}
于 2019-03-17T16:43:48.763 回答
0

这是我的解决方案,它parent_id首先对项目进行分组,然后使用分组列表从根递归地填充所有子分支以进行查找。

public function get_nested_tree() {
    $parent_node = null;
    $nodes_by_parent = array();
    
    if(is_null($flat_list) || count($flat_list) <= 0){
        return null;
    }

    foreach ($flat_list as $node) {
        if($node['parent_id'] != null){
            $nodes_by_parent[$node['parent_id']][] = $node;
        }
        else{
            // NB. In my implementation if multiple roots exist,
            // I want to always return the first...
            if(is_null($parent_node)){
                $parent_node = $node;
            }
        }
    }

    return $this->populate_branch($parent_node, $nodes_by_parent);
}

public function populate_branch($node, $nodes_by_parent){
    $children = $nodes_by_parent[$node['id']] ?? [];

    foreach ($children as &$child){
        $child = $this->populate_branch($child, $nodes_by_parent);
    }

    $node['children'] = $children;

    return $node;
}

我相信它的时间复杂度是线性的 ( O(n)) - 假设 PHP 关联数组等价于其他语言HashMapDictionary其他语言。

于 2020-11-09T13:04:45.413 回答