【问题标题】:create array tree from array list从数组列表创建数组树
【发布时间】:2011-05-10 22:00:09
【问题描述】:

我有一个这样的列表:

array(
  array(id=>100, parentid=>0, name=>'a'),
  array(id=>101, parentid=>100, name=>'a'),
  array(id=>102, parentid=>101, name=>'a'),
  array(id=>103, parentid=>101, name=>'a'),
)

但是要大得多,所以我需要一种有效的方法来将它变成像这样的树状结构:

array(
  id=>100, parentid=>0, name=>'a', children=>array(
    id=>101, parentid=>100, name=>'a', children=>array(
      id=>102, parentid=>101, name=>'a',
      id=>103, parentid=>101, name=>'a',
    )
  )
)

我不能使用嵌套集或类似的东西,因为我可以在我的数据库中添加左右值。 有任何想法吗?

【问题讨论】:

  • 没听懂...您的列表是 PHP 数组?
  • @andre OP 正在寻找邻接列表。这有很多重复。
  • 您演示的数组没有意义,因为您有重复的键。您的意思是有一个数组数组还是根据索引值显示隐含含义?
  • sry 数组中的拼写错误,但现在确实是数组数组编辑了它

标签: php arrays recursion tree


【解决方案1】:

这就是我解决它的方法:

$arr = array(
  array('id'=>100, 'parentid'=>0, 'name'=>'a'),
  array('id'=>101, 'parentid'=>100, 'name'=>'a'),
  array('id'=>102, 'parentid'=>101, 'name'=>'a'),
  array('id'=>103, 'parentid'=>101, 'name'=>'a'),
);

$new = array();
foreach ($arr as $a){
    $new[$a['parentid']][] = $a;
}
$tree = createTree($new, array($arr[0]));
print_r($tree);

function createTree(&$list, $parent){
    $tree = array();
    foreach ($parent as $k=>$l){
        if(isset($list[$l['id']])){
            $l['children'] = createTree($list, $list[$l['id']]);
        }
        $tree[] = $l;
    } 
    return $tree;
}

【讨论】:

  • 这很好用,只要确保,如果您有多个 parentid=0 的项目,循环遍历所有项目并检查 parentid == 0。如果是这样,然后运行 ​​createTree在该项目上并将其附加到您的树数组中。否则,此例程仅适用于 parentid=0 的第一项
  • 找到了这个解决方案,帮了大忙!
  • 问题:&$list 是必需的还是也可以与 $list 一起使用?我相信这就是 PHP 用来通过 ref 传递的。另外,PHP 中是否不鼓励递归?
  • @JinIzzraeel 如果你想通过 ref 传递非对象,你需要 & 符号。 PHP 不鼓励递归。
  • 你今天救了我免于拔头发。非常感谢。
【解决方案2】:

如果您需要超过 1 个 parentid[0] 元素的小修复 :)

$arr = array(
  array('id'=>100, 'parentid'=>0, 'name'=>'a'),
  array('id'=>101, 'parentid'=>100, 'name'=>'a'),
  array('id'=>102, 'parentid'=>101, 'name'=>'a'),
  array('id'=>103, 'parentid'=>101, 'name'=>'a'),
);

$new = array();
foreach ($arr as $a){
    $new[$a['parentid']][] = $a;
}
$tree = createTree($new, $new[0]); // changed
print_r($tree);

function createTree(&$list, $parent){
    $tree = array();
    foreach ($parent as $k=>$l){
        if(isset($list[$l['id']])){
            $l['children'] = createTree($list, $list[$l['id']]);
        }
        $tree[] = $l;
    } 
    return $tree;
}

【讨论】:

【解决方案3】:

Thunderstriker's variant 的再次修改 - 一个函数中的所有逻辑:

/**
 * @param array $flatList - a flat list of tree nodes; a node is an array with keys: id, parentID, name.
 */
function buildTree(array $flatList)
{
    $grouped = [];
    foreach ($flatList as $node){
        $grouped[$node['parentID']][] = $node;
    }

    $fnBuilder = function($siblings) use (&$fnBuilder, $grouped) {
        foreach ($siblings as $k => $sibling) {
            $id = $sibling['id'];
            if(isset($grouped[$id])) {
                $sibling['children'] = $fnBuilder($grouped[$id]);
            }
            $siblings[$k] = $sibling;
        }
        return $siblings;
    };

    return $fnBuilder($grouped[0]);
}

// Example:
$flat = [
    ['id' => 100, 'parentID' => 0, 'name' => 'root'],
    ['id' => 101, 'parentID' => 100, 'name' => 'ch-1'],
    ['id' => 102, 'parentID' => 101, 'name' => 'ch-1-1'],
    ['id' => 103, 'parentID' => 101, 'name' => 'ch-1-2'],
];

$tree = buildTree($flat, 'parentID', 'id');
echo json_encode($tree, JSON_PRETTY_PRINT);

游乐场:https://www.tehplayground.com/Ap4uUuwHWl9eiJIx

【讨论】:

  • 我喜欢这个例子,所以我将它封装在一个类中,并在 github 上提供; github.com/srayner/NavTree
  • 如何将一个元素作为特定父级的子级添加到此树中?
  • @HappyCoder 只需向 $flat 添加一个元素,例如 ['id'=>103, 'parentID'=>101, 'name'=>'a'] - 它是 ['id'=>101, 'parentID'=>100, 'name'=>'a'] 元素的子元素
  • 基于@Vasily 的回答,我制作了一个“实例数组”树构建器:gist.github.com/seniorpreacher/64adfcf4844974b568bc84bf3056c05e
  • 什么是idKey?我的 ide 一直在抱怨它
【解决方案4】:

这是我对arthur's rework的改编:

/* Recursive branch extrusion */
function createBranch(&$parents, $children) {
    $tree = array();
    foreach ($children as $child) {
        if (isset($parents[$child['id']])) {
            $child['children'] =
                $this->createBranch($parents, $parents[$child['id']]);
        }
        $tree[] = $child;
    } 
    return $tree;
}

/* Initialization */
function createTree($flat, $root = 0) {
    $parents = array();
    foreach ($flat as $a) {
        $parents[$a['parent']][] = $a;
    }
    return $this->createBranch($parents, $parents[$root]);
}

用途:

$tree = createTree($flat);

【讨论】:

  • 它与 id 为 0 的多个根父级完美配合。
【解决方案5】:

我创建了一个不寻常的(“基于while”而不是递归的)但多维排序函数,它遍历数组直到没有任何孤儿。这里的功能:

function treeze( &$a, $parent_key, $children_key )
{
    $orphans = true; $i;
    while( $orphans )
    {
        $orphans = false;
        foreach( $a as $k=>$v )
        {
            // is there $a[$k] sons?
            $sons = false;
            foreach( $a as $x=>$y )
            if( isset($y[$parent_key]) and $y[$parent_key]!=false and $y[$parent_key]==$k )  
            { 
                $sons=true; 
                $orphans=true; 
                break;
            }

            // $a[$k] is a son, without children, so i can move it
            if( !$sons and isset($v[$parent_key]) and $v[$parent_key]!=false )
            {
                $a[$v[$parent_key]][$children_key][$k] = $v;
                unset( $a[$k] );
            }
        }
    }
}

建议:数组中每个元素的key必须是元素本身的id。示例:

$ARRAY = array(
    1 => array( 'label' => "A" ),
    2 => array( 'label' => "B" ),
    3 => array( 'label' => "C" ),
    4 => array( 'label' => "D" ),
    5 => array( 'label' => "one", 'father' => '1' ),
    6 => array( 'label' => "two", 'father' => '1' ),
    7 => array( 'label' => "three", 'father' => '1' ),
    8 => array( 'label' => "node 1", 'father' => '2' ),
    9 => array( 'label' => "node 2", 'father' => '2' ),
    10 => array( 'label' => "node 3", 'father' => '2' ),
    11 => array( 'label' => "I", 'father' => '9' ),
    12 => array( 'label' => "II", 'father' => '9' ),
    13 => array( 'label' => "III", 'father' => '9' ),
    14 => array( 'label' => "IV", 'father' => '9' ),
    15 => array( 'label' => "V", 'father' => '9' ),
);

用法:函数需要$a(数组),$parent_key(父亲的id所在的列名),$children_key(父亲的id所在的列名)孩子们会搬家)。它不返回任何内容(数组通过引用更改)。示例:

treeze( $ARRAY, 'father', 'children' );
echo "<pre>"; print_r( $ARRAY );

【讨论】:

  • 好办法,比复发快!需要一点修正,“注意:未定义的索引:父亲”
  • @PeterKrauss 我已经修复了通知(并且还改进了一点格式)
【解决方案6】:

//if order by parentid, id
$arr = array(
    array('id'=>100, 'parentid'=>0, 'name'=>'a'),
    array('id'=>101, 'parentid'=>100, 'name'=>'a'),
    array('id'=>102, 'parentid'=>101, 'name'=>'a'),
    array('id'=>103, 'parentid'=>101, 'name'=>'a'),
);

$arr_tree = array();
$arr_tmp = array();

foreach ($arr as $item) {
    $parentid = $item['parentid'];
    $id = $item['id'];

    if ($parentid  == 0)
    {
        $arr_tree[$id] = $item;
        $arr_tmp[$id] = &$arr_tree[$id];
    }
    else 
    {
        if (!empty($arr_tmp[$parentid])) 
        {
            $arr_tmp[$parentid]['children'][$id] = $item;
            $arr_tmp[$id] = &$arr_tmp[$parentid]['children'][$id];
        }
    }
}

unset($arr_tmp);
echo '<pre>'; print_r($arr_tree); echo "</pre>";

【讨论】:

    【解决方案7】:

    执行此操作的一种方法是使用递归函数,该函数首先找到列表的所有底部值,并将它们添加到新数组中。然后对于每个新的 id,您对该 id 使用相同的函数,获取返回的数组并将其填充到该项目的新子数组中。最后,你返回你的新数组。

    我不会为你做所有的工作,但函数的参数看起来像:

    函数 recursiveChildren($items_array, $parent_id = 0)

    基本上,它会找到所有父项为 0 的项,然后对于每个项,它会找到所有以该 id 作为父项的项,然后对每个项.. 以此类推。

    最终的结果应该是你想要的。

    【讨论】:

    • 这个算法的问题是它很可能是 O(n^2)。考虑一个数组,其中每个元素都是下一个元素的父元素。该算法将扫描数组 n 次,产生 n(n+1)/2 次操作。
    • 所以在传递之前从旧数组中删除你找到的项目。我在这里的目的只是为了得到一个可以完成这项工作的函数的草图。不要快速完成工作。那是为了以后的优化。这是网络。缓存是消耗这些精神能量的更好地方。
    • 我对 n(n+1)/2 次操作的计算解释了每次扫描后数组都在缩小的事实。 OP 表示他的数据结构“更大”;我觉得暗示 O(n^2) 太贵了。
    • 我一直这样做,直到我的树变大,这就是我要求一种有效的方法,它可以在大约 1 秒内创建树,并且必须加载其中的几个,所以这个解决方案是慢。我发布了我的解决方案,这将在 5 毫秒内完成
    【解决方案8】:

    这三遍方法不起作用有什么原因吗?我没有做任何测试来比较一些递归解决方案的速度,但它似乎更直接。如果您的初始数组已经与作为键的 ID 相关联,那么您可以跳过第一个 foreach()

    function array_tree(&$array) {
        $tree = array();
    
        // Create an associative array with each key being the ID of the item
        foreach($array as $k => &$v) {
          $tree[$v['id']] = &$v;
        }
    
        // Loop over the array and add each child to their parent
        foreach($tree as $k => &$v) {
            if(!$v['parent']) {
              continue;
            }
            $tree[$v['parent']]['children'][] = &$v;
        }
    
        // Loop over the array again and remove any items that don't have a parent of 0;
        foreach($tree as $k => &$v) {
          if(!$v['parent']) {
            continue;
          }
          unset($tree[$k]);
        }
    
        return $tree;
    }
    

    【讨论】:

    • 就个人而言,我会让$array 不可变,只需将无父项添加到新数组中,然后返回该数组。
    【解决方案9】:

    更简单的版本:

    function build_tree(&$items, $parentId = null) {
        $treeItems = [];
        foreach ($items as $idx => $item) {
            if((empty($parentId) && empty($item['parent_id'])) || (!empty($item['parent_id']) && !empty($parentId) && $item['parent_id'] == $parentId)) {
                $items[$idx]['children'] = build_tree($items, $items[$idx]['id']);
                $treeItems []= $items[$idx];
            }
        }
    
        return $treeItems;
    }
    build_tree($array);
    

    【讨论】:

      猜你喜欢
      • 2016-08-08
      • 1970-01-01
      • 1970-01-01
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-07
      • 1970-01-01
      相关资源
      最近更新 更多