将一系列父子关系转换为分层树?

时间:2021-06-28 08:18:48

I have a bunch of name-parentname pairs, that I'd like to turn into as few heirarchical tree structures as possible. So for example, these could be the pairings:

我有一堆名字 - 父对名字对,我想尽可能少的层次结构树结构。例如,这些可能是配对:

Child : Parent    H : G    F : G    G : D    E : D    A : E    B : C    C : E    D : NULL

Which needs to be transformed into (a) heirarchical tree(s):

需要转化为(a)层次结构树:

D├── E│   ├── A│   │   └── B│   └── C   └── G    ├── F    └── H

The end result that I want is a nested set of <ul> elements, with each <li> containing the child's name.

我想要的最终结果是一组嵌套的

    元素,每个
  • 包含子名称。

There are no inconsistencies in the pairings (child is it's own parent, parent is child's child, etc), so a bunch of optimizations can probably be made.

配对中没有不一致(孩子是自己的父母,父母是孩子的孩子等),因此可能会进行一系列的优化。

How, in PHP, would I go from an array containing child=>parent pairs, to a set of Nested <ul>s?

在PHP中,我如何从包含child => parent对的数组转到一组嵌套

    s?

I have a feeling that recursion is involved, but I'm not quite awake enough to think it through.

我有一种感觉,涉及到递归,但我还没有完全清醒地思考它。

8 个解决方案

#1


This requires a very basic recursive function to parse the child/parent pairs to a tree structure and another recursive function to print it out. Only one function would suffice but here's two for clarity (a combined function can be found at the end of this answer).

这需要一个非常基本的递归函数来将子/父对解析为树结构,另一个递归函数将其打印出来。只有一个函数就足够了,但为了清楚起见,这里有两个函数(在这个答案的最后可以找到一个组合函数)。

First initialize the array of child/parent pairs:

首先初始化子/父对的数组:

$tree = array(    'H' => 'G',    'F' => 'G',    'G' => 'D',    'E' => 'D',    'A' => 'E',    'B' => 'C',    'C' => 'E',    'D' => null);

Then the function that parses that array into a hierarchical tree structure:

然后将该数组解析为分层树结构的函数:

function parseTree($tree, $root = null) {    $return = array();    # Traverse the tree and search for direct children of the root    foreach($tree as $child => $parent) {        # A direct child is found        if($parent == $root) {            # Remove item from tree (we don't need to traverse this again)            unset($tree[$child]);            # Append the child into result array and parse its children            $return[] = array(                'name' => $child,                'children' => parseTree($tree, $child)            );        }    }    return empty($return) ? null : $return;    }

And a function that traverses that tree to print out an unordered list:

并且遍历该树以打印出无序列表的函数:

function printTree($tree) {    if(!is_null($tree) && count($tree) > 0) {        echo '<ul>';        foreach($tree as $node) {            echo '<li>'.$node['name'];            printTree($node['children']);            echo '</li>';        }        echo '</ul>';    }}

And the actual usage:

而实际用途:

$result = parseTree($tree);printTree($result);

Here's the contents of $result:

这是$ result的内容:

Array(    [0] => Array(        [name] => D        [children] => Array(            [0] => Array(                [name] => G                [children] => Array(                    [0] => Array(                        [name] => H                        [children] => NULL                    )                    [1] => Array(                        [name] => F                        [children] => NULL                    )                )            )            [1] => Array(                [name] => E                [children] => Array(                    [0] => Array(                        [name] => A                        [children] => NULL                    )                    [1] => Array(                        [name] => C                        [children] => Array(                            [0] => Array(                                [name] => B                                [children] => NULL                            )                        )                    )                )            )        )    ))

If you want a bit more efficiency, you can combine those functions into one and reduce the number of iterations made:

如果您想要更高效率,可以将这些功能合并为一个并减少迭代次数:

function parseAndPrintTree($root, $tree) {    $return = array();    if(!is_null($tree) && count($tree) > 0) {        echo '<ul>';        foreach($tree as $child => $parent) {            if($parent == $root) {                                    unset($tree[$child]);                echo '<li>'.$child;                parseAndPrintTree($child, $tree);                echo '</li>';            }        }        echo '</ul>';    }}

You'll only save 8 iterations on a dataset as small as this but on bigger sets it could make a difference.

你只会在数据集上保存8次迭代,但是在更大的集合上,它可能会有所不同。

#2


Yet Another Function To Make A Tree (no recursion involved, uses references instead):

还有另一个创建树的函数(不涉及递归,而是使用引用):

$array = array('H' => 'G', 'F' => 'G', ..., 'D' => null);function to_tree($array){    $flat = array();    $tree = array();    foreach ($array as $child => $parent) {        if (!isset($flat[$child])) {            $flat[$child] = array();        }        if (!empty($parent)) {            $flat[$parent][$child] =& $flat[$child];        } else {            $tree[$child] =& $flat[$child];        }    }    return $tree;}

Returns an hierarchical array like this one:

返回一个像这样的分层数组:

Array(    [D] => Array(        [G] => Array(            [H] => Array()            [F] => Array()        )        ...    ))

Which can easily be printed as a HTML list using recursive function.

可以使用递归函数轻松打印为HTML列表。

#3


Another, more simplified way to convert the flat structure in the $tree into a hierarchy. Only one temporary array is needed to expose it:

将$ tree中的扁平结构转换为层次结构的另一种更简化的方法。只需要一个临时数组来公开它:

// add children to parents$flat = array(); # temporary arrayforeach ($tree as $name => $parent){    $flat[$name]['name'] = $name; # self    if (NULL === $parent)    {        # no parent, is root element, assign it to $tree        $tree = &$flat[$name];     }    else    {        # has parent, add self as child            $flat[$parent]['children'][] = &$flat[$name];    }}unset($flat);

That's all for getting the hierarchy into a multidimensional array:

这就是将层次结构变为多维数组的全部内容:

Array(    [children] => Array        (            [0] => Array                (                    [children] => Array                        (                            [0] => Array                                (                                    [name] => H                                )                            [1] => Array                                (                                    [name] => F                                )                        )                    [name] => G                )            [1] => Array                (                    [name] => E                    [children] => Array                        (                            [0] => Array                                (                                    [name] => A                                )                            [1] => Array                                (                                    [children] => Array                                        (                                            [0] => Array                                                (                                                    [name] => B                                                )                                        )                                    [name] => C                                )                        )                )        )    [name] => D)

The output is less trivial if you want to avoid recursion (can be a burden with large structures).

如果你想避免递归,输出就不那么简单了(对于大型结构来说可能是一种负担)。

I always wanted to solve the UL/LI "dilemma" for outputting an array. The dilemma is, that each item does not know whether or not children will follow up or how many preceding elements need to be closed. In another answer I already solved that by using a RecursiveIteratorIterator and looking for getDepth() and other meta-information that my own written Iterator provided: Getting nested set model into a <ul> but hiding “closed” subtrees. That answer shows as well that with iterators you're quite flexible.

我一直想解决输出数组的UL / LI“困境”。困境是,每个项目都不知道孩子是否会跟进或需要关闭多少先前元素。在另一个答案中,我已经通过使用RecursiveIteratorIterator并查找我自己编写的迭代器提供的getDepth()和其他元信息来解决这个问题:将嵌套集模型转换为

    但隐藏“封闭”子树。答案也表明,使用迭代器你会非常灵活。

However that was a pre-sorted list, so would not be suitable for your example. Additionally I always wanted to solve this for a sort of standard tree structure and HTML's <ul> and <li> elements.

然而,这是一个预先排序的列表,因此不适合您的示例。另外,我一直想为一种标准树结构和HTML的

  • 元素解决这个问题。

The basic concept I came up is the following:

我提出的基本概念如下:

  1. TreeNode - Abstracts each element into a simple TreeNode type that can provide it's value (e.g. Name) and whether or not it has children.
  2. TreeNode - 将每个元素抽象为一个简单的TreeNode类型,该类型可以提供它的值(例如Name)以及它是否有子节点。

  3. TreeNodesIterator - A RecursiveIterator that is able to iterate over a set (array) of these TreeNodes. That is fairly simple as the TreeNode type already knows if it has children and which ones.
  4. TreeNodesIterator - 一个能够迭代这些TreeNode的集合(数组)的RecursiveIterator。这很简单,因为TreeNode类型已经知道它是否有子节点和哪些节点。

  5. RecursiveListIterator - A RecursiveIteratorIterator that has all the events needed when it recursively iterate over any kind of RecursiveIterator:
    • beginIteration / endIteration - Begin and end of the main list.
    • beginIteration / endIteration - 主列表的开头和结尾。

    • beginElement / endElement - Begin and end of each element.
    • beginElement / endElement - 每个元素的开头和结尾。

    • beginChildren / endChildren - Begin and end of each children list.This RecursiveListIterator only provides these events in a form of function calls. children lists, as it is typical for <ul><li> lists, are opened and closed inside it's parent <li> element. Therefore the endElement event is fired after the according endChildren event. This could be changed or made configurable to broaden the use this class. The events are distributed as function calls to a decorator object then, to keep things apart.
    • beginChildren / endChildren - 每个子列表的开头和结尾。此RecursiveListIterator仅以函数调用的形式提供这些事件。子列表,因为它是典型的

      • 列表,在其父
      • 元素内打开和关闭。因此,在endChildren事件之后触发endElement事件。这可以更改或可配置以扩大此类的使用范围。事件作为函数调用分发给装饰器对象,然后将事物分开。

  6. RecursiveListIterator - 一个RecursiveIteratorIterator,它具有递归迭代任何类型的RecursiveIterator时所需的所有事件:beginIteration / endIteration - 主列表的开始和结束。 beginElement / endElement - 每个元素的开头和结尾。 beginChildren / endChildren - 每个子列表的开头和结尾。此RecursiveListIterator仅以函数调用的形式提供这些事件。子列表,因为它是典型的

  • 列表,在其父
  • 元素内打开和关闭。因此,在endChildren事件之后触发endElement事件。这可以更改或可配置以扩大此类的使用范围。事件作为函数调用分发给装饰器对象,然后将事物分开。

  • ListDecorator - A "decorator" class that is just a receiver of the events of RecursiveListIterator.
  • ListDecorator - 一个“装饰器”类,它只是RecursiveListIterator事件的接收者。

    I start with the main output logic. Taken the now hierarchical $tree array, the final code looks like the following:

    我从主输出逻辑开始。采用现在分层的$ tree数组,最终代码如下所示:

    $root = new TreeNode($tree);$it = new TreeNodesIterator(array($root));$rit = new RecursiveListIterator($it);$decor = new ListDecorator($rit);$rit->addDecorator($decor);foreach($rit as $item){    $inset = $decor->inset(1);    printf("%s%s\n", $inset, $item->getName());}

    First let's look into the ListDecorator that simply wraps the <ul> and <li> elements and is deciding about how the list structure is output:

    首先让我们看一下ListDecorator,它简单地包装

    • 元素,并决定如何输出列表结构:

    class ListDecorator{    private $iterator;    public function __construct(RecursiveListIterator $iterator)    {        $this->iterator = $iterator;    }    public function inset($add = 0)    {        return str_repeat('  ', $this->iterator->getDepth()*2+$add);    }

    The constructor takes the list iterator it's working on. inset is just a helper function for nice indentation of the output. The rest are just the output functions for each event:

    构造函数接受它正在处理的列表迭代器。 inset只是一个帮助函数,可以很好地缩进输出。其余的只是每个事件的输出函数:

        public function beginElement()    {        printf("%s<li>\n", $this->inset());    }    public function endElement()    {        printf("%s</li>\n", $this->inset());    }    public function beginChildren()    {        printf("%s<ul>\n", $this->inset(-1));    }    public function endChildren()    {        printf("%s</ul>\n", $this->inset(-1));    }    public function beginIteration()    {        printf("%s<ul>\n", $this->inset());    }    public function endIteration()    {        printf("%s</ul>\n", $this->inset());    }}

    With these output functions in mind, this is the main output wrap-up / loop again, I go through it step by step:

    考虑到这些输出功能,这又是主要的输出总结/循环,我一步一步地完成它:

    $root = new TreeNode($tree);

    Create the root TreeNode which will be used to start iteration upon:

    创建将用于开始迭代的根TreeNode:

    $it = new TreeNodesIterator(array($root));

    This TreeNodesIterator is a RecursiveIterator that enables recursive iteration over the single $root node. It is passed as an array because that class needs something to iterate over and allows re-use with a set of children which is also an array of TreeNode elements.

    此TreeNodesIterator是一个RecursiveIterator,它允许在单个$ root节点上进行递归迭代。它作为一个数组传递,因为该类需要迭代一些东西并允许与一组子节点重用,这些子节点也是TreeNode元素的数组。

    $rit = new RecursiveListIterator($it);

    This RecursiveListIterator is a RecursiveIteratorIterator that provides the said events. To make use of it, only a ListDecorator needs to be provided (the class above) and assigned with addDecorator:

    此RecursiveListIterator是一个RecursiveIteratorIterator,它提供所述事件。要使用它,只需要提供ListDecorator(上面的类)并使用addDecorator分配:

    $decor = new ListDecorator($rit);$rit->addDecorator($decor);

    Then everything is set-up to just foreach over it and output each node:

    然后,所有内容都设置为只是对它进行预设并输出每个节点:

    foreach($rit as $item){    $inset = $decor->inset(1);    printf("%s%s\n", $inset, $item->getName());}

    As this example shows, the whole output logic is encapsulated in the ListDecorator class and this single foreach. The whole recursive traversal has been fully encapsulated into SPL recursive iterators which provided a stacked procedure, that means internally no recursion function calls are done.

    如此示例所示,整个输出逻辑封装在ListDecorator类和此单个foreach中。整个递归遍历已经完全封装到SPL递归迭代器中,它提供了一个堆栈过程,这意味着内部没有完成递归函数调用。

    The event based ListDecorator allows you to modify the output specifically and to provide multiple type of lists for the same data structure. It's even possible to change the input as the array data has been encapsulated into TreeNode.

    基于事件的ListDecorator允许您专门修改输出并为同一数据结构提供多种类型的列表。甚至可以在将数组数据封装到TreeNode中时更改输入。

    The full code example:

    完整的代码示例:

    <?phpnamespace My;$tree = array('H' => 'G', 'F' => 'G', 'G' => 'D', 'E' => 'D', 'A' => 'E', 'B' => 'C', 'C' => 'E', 'D' => null);// add children to parents$flat = array(); # temporary arrayforeach ($tree as $name => $parent){    $flat[$name]['name'] = $name; # self    if (NULL === $parent)    {        # no parent, is root element, assign it to $tree        $tree = &$flat[$name];    }    else    {        # has parent, add self as child            $flat[$parent]['children'][] = &$flat[$name];    }}unset($flat);class TreeNode{    protected $data;    public function __construct(array $element)    {        if (!isset($element['name']))            throw new InvalidArgumentException('Element has no name.');        if (isset($element['children']) && !is_array($element['children']))            throw new InvalidArgumentException('Element has invalid children.');        $this->data = $element;    }    public function getName()    {         return $this->data['name'];    }    public function hasChildren()    {        return isset($this->data['children']) && count($this->data['children']);    }    /**     * @return array of child TreeNode elements      */    public function getChildren()    {                $children = $this->hasChildren() ? $this->data['children'] : array();        $class = get_called_class();        foreach($children as &$element)        {            $element = new $class($element);        }        unset($element);                return $children;    }}class TreeNodesIterator implements \RecursiveIterator{    private $nodes;    public function __construct(array $nodes)    {        $this->nodes = new \ArrayIterator($nodes);    }    public function  getInnerIterator()    {        return $this->nodes;    }    public function getChildren()    {        return new TreeNodesIterator($this->nodes->current()->getChildren());    }    public function hasChildren()    {        return $this->nodes->current()->hasChildren();    }    public function rewind()    {        $this->nodes->rewind();    }    public function valid()    {        return $this->nodes->valid();    }       public function current()    {        return $this->nodes->current();    }    public function key()    {        return $this->nodes->key();    }    public function next()    {        return $this->nodes->next();    }}class RecursiveListIterator extends \RecursiveIteratorIterator{    private $elements;    /**     * @var ListDecorator     */    private $decorator;    public function addDecorator(ListDecorator $decorator)    {        $this->decorator = $decorator;    }    public function __construct($iterator, $mode = \RecursiveIteratorIterator::SELF_FIRST, $flags = 0)    {        parent::__construct($iterator, $mode, $flags);    }    private function event($name)    {        // event debug code: printf("--- %'.-20s --- (Depth: %d, Element: %d)\n", $name, $this->getDepth(), @$this->elements[$this->getDepth()]);        $callback = array($this->decorator, $name);        is_callable($callback) && call_user_func($callback);    }    public function beginElement()    {        $this->event('beginElement');    }    public function beginChildren()    {        $this->event('beginChildren');    }    public function endChildren()    {        $this->testEndElement();        $this->event('endChildren');    }    private function testEndElement($depthOffset = 0)    {        $depth = $this->getDepth() + $depthOffset;              isset($this->elements[$depth]) || $this->elements[$depth] = 0;        $this->elements[$depth] && $this->event('endElement');    }    public function nextElement()    {        $this->testEndElement();        $this->event('{nextElement}');        $this->event('beginElement');               $this->elements[$this->getDepth()] = 1;    }     public function beginIteration()    {        $this->event('beginIteration');    }    public function endIteration()    {        $this->testEndElement();        $this->event('endIteration');           }}class ListDecorator{    private $iterator;    public function __construct(RecursiveListIterator $iterator)    {        $this->iterator = $iterator;    }    public function inset($add = 0)    {        return str_repeat('  ', $this->iterator->getDepth()*2+$add);    }    public function beginElement()    {        printf("%s<li>\n", $this->inset(1));    }    public function endElement()    {        printf("%s</li>\n", $this->inset(1));    }    public function beginChildren()    {        printf("%s<ul>\n", $this->inset());    }    public function endChildren()    {        printf("%s</ul>\n", $this->inset());    }    public function beginIteration()    {        printf("%s<ul>\n", $this->inset());    }    public function endIteration()    {        printf("%s</ul>\n", $this->inset());    }}$root = new TreeNode($tree);$it = new TreeNodesIterator(array($root));$rit = new RecursiveListIterator($it);$decor = new ListDecorator($rit);$rit->addDecorator($decor);foreach($rit as $item){    $inset = $decor->inset(2);    printf("%s%s\n", $inset, $item->getName());}

    Outpupt:

    <ul>  <li>    D    <ul>      <li>        G        <ul>          <li>            H          </li>          <li>            F          </li>        </ul>      </li>      <li>        E        <ul>          </li>          <li>            A          </li>          <li>            C            <ul>              <li>                B              </li>            </ul>          </li>        </ul>      </li>    </ul>  </li></ul>

    Demo (PHP 5.2 variant)

    演示(PHP 5.2变体)

    A possible variant would be an iterator that iterates over any RecursiveIterator and provides an iteration over all events that can occur. An switch / case inside the foreach loop could then deal with the events.

    一个可能的变体是迭代器迭代任何RecursiveIterator并提供可能发生的所有事件的迭代。 foreach循环内的开关/案例可以处理事件。

    Related:

    #4


    Well, first I would turn the straight array of key-value pairs into a hierarchical array

    好吧,首先我将键值对的直接数组转换为分层数组

    function convertToHeiarchical(array $input) {    $parents = array();    $root = array();    $children = array();    foreach ($input as $item) {        $parents[$item['id']] = &$item;        if ($item['parent_id']) {            if (!isset($children[$item['parent_id']])) {                $children[$item['parent_id']] = array();            }            $children[$item['parent_id']][] = &$item;        } else {            $root = $item['id'];        }    }    foreach ($parents as $id => &$item) {        if (isset($children[$id])) {            $item['children'] = $children[$id];        } else {            $item['children'] = array();        }    }    return $parents[$root];}

    That'll can convert a flat array with parent_id and id into a hierarchical one:

    那可以将带有parent_id和id的平面数组转换为分层数组:

    $item = array(    'id' => 'A',    'blah' => 'blah',    'children' => array(        array(            'id' => 'B',            'blah' => 'blah',            'children' => array(                array(                    'id' => 'C',                    'blah' => 'blah',                    'children' => array(),                ),             ),            'id' => 'D',            'blah' => 'blah',            'children' => array(                array(                    'id' => 'E',                    'blah' => 'blah',                    'children' => array(),                ),            ),        ),    ),);

    Then, just create a rendering function:

    然后,只需创建一个渲染函数:

    function renderItem($item) {    $out = "Your OUtput For Each Item Here";    $out .= "<ul>";    foreach ($item['children'] as $child) {        $out .= "<li>".renderItem($child)."</li>";    }    $out .= "</ul>";    return $out;}

    #5


    While Alexander-Konstantinov's solution might not seem as easy to read at first, it is both genius and exponentially better in terms of performance, this should have been voted as the best answer.

    虽然亚历山大 - 康斯坦丁诺夫的解决方案起初可能看起来不那么容易,但就性能而言,它既是天才又是指数级的,这应该被认为是最好的答案。

    Thanks mate, I made a benchmark in your honor to compare the 2 solutions presented in this post.

    谢谢你的配偶,我为你的荣誉做了一个基准,比较了这篇文章中提出的两个解决方案。

    I had an @250k flat tree with 6 levels that I had to convert and I was searching for a better way to do so and avoid recursive iterations.

    我有一个@ 250k平面树,有6个级别,我必须转换,我正在寻找一个更好的方法这样做,并避免递归迭代。

    Recursion vs Reference:

    递归与参考:

    // Generate a 6 level flat tree$root = null;$lvl1 = 13;$lvl2 = 11;$lvl3 = 7;$lvl4 = 5;$lvl5 = 3;$lvl6 = 1;    $flatTree = [];for ($i = 1; $i <= 450000; $i++) {    if ($i % 3 == 0)  { $lvl5 = $i; $flatTree[$lvl6] = $lvl5; continue; }    if ($i % 5 == 0)  { $lvl4 = $i; $flatTree[$lvl5] = $lvl4; continue; }    if ($i % 7 == 0)  { $lvl3 = $i; $flatTree[$lvl3] = $lvl2; continue; }    if ($i % 11 == 0) { $lvl2 = $i; $flatTree[$lvl2] = $lvl1; continue; }    if ($i % 13 == 0) { $lvl1 = $i; $flatTree[$lvl1] = $root; continue; }    $lvl6 = $i;}echo 'Array count: ', count($flatTree), PHP_EOL;// Reference functionfunction treeByReference($flatTree){    $flat = [];    $tree = [];    foreach ($flatTree as $child => $parent) {        if (!isset($flat[$child])) {            $flat[$child] = [];        }        if (!empty($parent)) {            $flat[$parent][$child] =& $flat[$child];        } else {            $tree[$child] =& $flat[$child];        }    }    return $tree;}// Recursion functionfunction treeByRecursion($flatTree, $root = null){    $return = [];    foreach($flatTree as $child => $parent) {        if ($parent == $root) {            unset($flatTree[$child]);            $return[$child] = treeByRecursion($flatTree, $child);        }    }    return $return ?: [];}// Benchmark reference$t1 = microtime(true);$tree = treeByReference($flatTree);echo 'Reference: ', (microtime(true) - $t1), PHP_EOL;// Benchmark recursion$t2 = microtime(true);$tree = treeByRecursion($flatTree);echo 'Recursion: ', (microtime(true) - $t2), PHP_EOL;

    The output speaks for itself:

    输出不言自明:

    Array count: 255493Reference: 0.3259289264679 (less than 0.4s)Recursion: 6604.9865279198 (almost 2h)

    #6


    Well, to parse into ULs and LIs, it would be something like:

    那么,要解析为UL和LI,它将是这样的:

    $array = array (    'H' => 'G'    'F' => 'G'    'G' => 'D'    'E' => 'D'    'A' => 'E'    'B' => 'C'    'C' => 'E'    'D' => 'NULL');recurse_uls ($array, 'NULL');function recurse_uls ($array, $parent){    echo '<ul>';    foreach ($array as $c => $p)  {        if ($p != $parent) continue;        echo '<li>'.$c.'</li>';        recurse_uls ($array, $c);    }    echo '</ul>';}

    But I'd love to see a solution that doesn't require you to iterate through the array so often...

    但我很乐意看到一个解决方案,不需要你经常迭代数组......

    #7


    Here's what I came up with:

    这是我想出的:

    $arr = array(            'H' => 'G',            'F' => 'G',            'G' => 'D',            'E' => 'D',            'A' => 'E',            'B' => 'C',            'C' => 'E',            'D' => null );    $nested = parentChild($arr);    print_r($nested);    function parentChild(&$arr, $parent = false) {      if( !$parent) { //initial call         $rootKey = array_search( null, $arr);         return array($rootKey => parentChild($arr, $rootKey));      }else { // recursing through        $keys = array_keys($arr, $parent);        $piece = array();        if($keys) { // found children, so handle them          if( !is_array($keys) ) { // only one child            $piece = parentChild($arr, $keys);           }else{ // multiple children             foreach( $keys as $key ){               $piece[$key] = parentChild($arr, $key);             }           }        }else {           return $parent; //return the main tag (no kids)        }        return $piece; // return the array built via recursion      }    }

    outputs:

    Array(    [D] => Array        (            [G] => Array                (                    [H] => H                    [F] => F                )            [E] => Array                (                    [A] => A                    [C] => Array                        (                            [B] => B                        )                    )            )    )

    #8


    How to Create Dynamic Tree View and Menu

    如何创建动态树视图和菜单

    Step 1:First we will Create treeview table in mysql database. this table contains four column.id is the task id and name is the task name.

    第1步:首先我们将在mysql数据库中创建treeview表。此表包含四个column.id是任务ID,name是任务名称。

    --- Table structure for table `treeview_items`--CREATE TABLE IF NOT EXISTS `treeview_items` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(200) NOT NULL,  `title` varchar(200) NOT NULL,  `parent_id` varchar(11) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;---- Dumping data for table `treeview_items`--INSERT INTO `treeview_items` (`id`, `name`, `title`, `parent_id`) VALUES(1, 'task1', 'task1title', '2'),(2, 'task2', 'task2title', '0'),(3, 'task3', 'task1title3', '0'),(4, 'task4', 'task2title4', '3'),(5, 'task4', 'task1title4', '3'),(6, 'task5', 'task2title5', '5');

    Step 2: Tree view recursive methodI have created below tree createTreeView() method which call recursive if current task id is greater than prev task id.

    第2步:树视图递归方法我创建了下面的树createTreeView()方法,如果当前任务id大于prev任务id,则调用recursive。

    function createTreeView($array, $currentParent, $currLevel = 0, $prevLevel = -1) {foreach ($array as $categoryId => $category) {if ($currentParent == $category['parent_id']) {                           if ($currLevel > $prevLevel) echo " <ol class='tree'> ";     if ($currLevel == $prevLevel) echo " </li> ";    echo '<li> <label for="subfolder2">'.$category['name'].'</label> <input type="checkbox" name="subfolder2"/>';    if ($currLevel > $prevLevel) { $prevLevel = $currLevel; }    $currLevel++;     createTreeView ($array, $categoryId, $currLevel, $prevLevel);    $currLevel--;                   }   }if ($currLevel == $prevLevel) echo " </li>  </ol> ";}

    Step 3: Create index file to show tree view.This is main file of treeview example here we will call createTreeView() method with required parameters.

    步骤3:创建索引文件以显示树视图。这是树视图示例的主文件,我们将使用所需参数调用createTreeView()方法。

     <body><link rel="stylesheet" type="text/css" href="_styles.css" media="screen"><?phpmysql_connect('localhost', 'root');mysql_select_db('test');$qry="SELECT * FROM treeview_items";$result=mysql_query($qry);$arrayCategories = array();while($row = mysql_fetch_assoc($result)){  $arrayCategories[$row['id']] = array("parent_id" => $row['parent_id'], "name" =>                        $row['name']);     }?><div id="content" class="general-style1"><?phpif(mysql_num_rows($result)!=0){?><?php createTreeView($arrayCategories, 0); ?><?php}?></div></body>

    Step 4: Create CSS file style.cssHere we will write all css related class, currently I am using order list to create tree view. you can also change image path here.

    第4步:创建CSS文件style.cssHere我们将编写所有css相关的类,目前我正在使用order list创建树视图。你也可以在这里改变图像路径。

    img { border: none; }input, select, textarea, th, td { font-size: 1em; }/* CSS Tree menu styles */ol.tree{    padding: 0 0 0 30px;    width: 300px;}    li     {         position: relative;         margin-left: -15px;        list-style: none;    }    li.file    {        margin-left: -1px !important;    }        li.file a        {            background: url(document.png) 0 0 no-repeat;            color: #fff;            padding-left: 21px;            text-decoration: none;            display: block;        }        li.file a[href *= '.pdf']   { background: url(document.png) 0 0 no-repeat; }        li.file a[href *= '.html']  { background: url(document.png) 0 0 no-repeat; }        li.file a[href $= '.css']   { background: url(document.png) 0 0 no-repeat; }        li.file a[href $= '.js']        { background: url(document.png) 0 0 no-repeat; }    li input    {        position: absolute;        left: 0;        margin-left: 0;        opacity: 0;        z-index: 2;        cursor: pointer;        height: 1em;        width: 1em;        top: 0;    }        li input + ol        {            background: url(toggle-small-expand.png) 40px 0 no-repeat;            margin: -0.938em 0 0 -44px; /* 15px */            height: 1em;        }        li input + ol > li { display: none; margin-left: -14px !important; padding-left: 1px; }    li label    {        background: url(folder-horizontal.png) 15px 1px no-repeat;        cursor: pointer;        display: block;        padding-left: 37px;    }    li input:checked + ol    {        background: url(toggle-small.png) 40px 5px no-repeat;        margin: -1.25em 0 0 -44px; /* 20px */        padding: 1.563em 0 0 80px;        height: auto;    }        li input:checked + ol > li { display: block; margin: 0 0 0.125em;  /* 2px */}        li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ }

    More Details

    #1


    This requires a very basic recursive function to parse the child/parent pairs to a tree structure and another recursive function to print it out. Only one function would suffice but here's two for clarity (a combined function can be found at the end of this answer).

    这需要一个非常基本的递归函数来将子/父对解析为树结构,另一个递归函数将其打印出来。只有一个函数就足够了,但为了清楚起见,这里有两个函数(在这个答案的最后可以找到一个组合函数)。

    First initialize the array of child/parent pairs:

    首先初始化子/父对的数组:

    $tree = array(    'H' => 'G',    'F' => 'G',    'G' => 'D',    'E' => 'D',    'A' => 'E',    'B' => 'C',    'C' => 'E',    'D' => null);

    Then the function that parses that array into a hierarchical tree structure:

    然后将该数组解析为分层树结构的函数:

    function parseTree($tree, $root = null) {    $return = array();    # Traverse the tree and search for direct children of the root    foreach($tree as $child => $parent) {        # A direct child is found        if($parent == $root) {            # Remove item from tree (we don't need to traverse this again)            unset($tree[$child]);            # Append the child into result array and parse its children            $return[] = array(                'name' => $child,                'children' => parseTree($tree, $child)            );        }    }    return empty($return) ? null : $return;    }

    And a function that traverses that tree to print out an unordered list:

    并且遍历该树以打印出无序列表的函数:

    function printTree($tree) {    if(!is_null($tree) && count($tree) > 0) {        echo '<ul>';        foreach($tree as $node) {            echo '<li>'.$node['name'];            printTree($node['children']);            echo '</li>';        }        echo '</ul>';    }}

    And the actual usage:

    而实际用途:

    $result = parseTree($tree);printTree($result);

    Here's the contents of $result:

    这是$ result的内容:

    Array(    [0] => Array(        [name] => D        [children] => Array(            [0] => Array(                [name] => G                [children] => Array(                    [0] => Array(                        [name] => H                        [children] => NULL                    )                    [1] => Array(                        [name] => F                        [children] => NULL                    )                )            )            [1] => Array(                [name] => E                [children] => Array(                    [0] => Array(                        [name] => A                        [children] => NULL                    )                    [1] => Array(                        [name] => C                        [children] => Array(                            [0] => Array(                                [name] => B                                [children] => NULL                            )                        )                    )                )            )        )    ))

    If you want a bit more efficiency, you can combine those functions into one and reduce the number of iterations made:

    如果您想要更高效率,可以将这些功能合并为一个并减少迭代次数:

    function parseAndPrintTree($root, $tree) {    $return = array();    if(!is_null($tree) && count($tree) > 0) {        echo '<ul>';        foreach($tree as $child => $parent) {            if($parent == $root) {                                    unset($tree[$child]);                echo '<li>'.$child;                parseAndPrintTree($child, $tree);                echo '</li>';            }        }        echo '</ul>';    }}

    You'll only save 8 iterations on a dataset as small as this but on bigger sets it could make a difference.

    你只会在数据集上保存8次迭代,但是在更大的集合上,它可能会有所不同。

    #2


    Yet Another Function To Make A Tree (no recursion involved, uses references instead):

    还有另一个创建树的函数(不涉及递归,而是使用引用):

    $array = array('H' => 'G', 'F' => 'G', ..., 'D' => null);function to_tree($array){    $flat = array();    $tree = array();    foreach ($array as $child => $parent) {        if (!isset($flat[$child])) {            $flat[$child] = array();        }        if (!empty($parent)) {            $flat[$parent][$child] =& $flat[$child];        } else {            $tree[$child] =& $flat[$child];        }    }    return $tree;}

    Returns an hierarchical array like this one:

    返回一个像这样的分层数组:

    Array(    [D] => Array(        [G] => Array(            [H] => Array()            [F] => Array()        )        ...    ))

    Which can easily be printed as a HTML list using recursive function.

    可以使用递归函数轻松打印为HTML列表。

    #3


    Another, more simplified way to convert the flat structure in the $tree into a hierarchy. Only one temporary array is needed to expose it:

    将$ tree中的扁平结构转换为层次结构的另一种更简化的方法。只需要一个临时数组来公开它:

    // add children to parents$flat = array(); # temporary arrayforeach ($tree as $name => $parent){    $flat[$name]['name'] = $name; # self    if (NULL === $parent)    {        # no parent, is root element, assign it to $tree        $tree = &$flat[$name];     }    else    {        # has parent, add self as child            $flat[$parent]['children'][] = &$flat[$name];    }}unset($flat);

    That's all for getting the hierarchy into a multidimensional array:

    这就是将层次结构变为多维数组的全部内容:

    Array(    [children] => Array        (            [0] => Array                (                    [children] => Array                        (                            [0] => Array                                (                                    [name] => H                                )                            [1] => Array                                (                                    [name] => F                                )                        )                    [name] => G                )            [1] => Array                (                    [name] => E                    [children] => Array                        (                            [0] => Array                                (                                    [name] => A                                )                            [1] => Array                                (                                    [children] => Array                                        (                                            [0] => Array                                                (                                                    [name] => B                                                )                                        )                                    [name] => C                                )                        )                )        )    [name] => D)

    The output is less trivial if you want to avoid recursion (can be a burden with large structures).

    如果你想避免递归,输出就不那么简单了(对于大型结构来说可能是一种负担)。

    I always wanted to solve the UL/LI "dilemma" for outputting an array. The dilemma is, that each item does not know whether or not children will follow up or how many preceding elements need to be closed. In another answer I already solved that by using a RecursiveIteratorIterator and looking for getDepth() and other meta-information that my own written Iterator provided: Getting nested set model into a <ul> but hiding “closed” subtrees. That answer shows as well that with iterators you're quite flexible.

    我一直想解决输出数组的UL / LI“困境”。困境是,每个项目都不知道孩子是否会跟进或需要关闭多少先前元素。在另一个答案中,我已经通过使用RecursiveIteratorIterator并查找我自己编写的迭代器提供的getDepth()和其他元信息来解决这个问题:将嵌套集模型转换为

      但隐藏“封闭”子树。答案也表明,使用迭代器你会非常灵活。

    However that was a pre-sorted list, so would not be suitable for your example. Additionally I always wanted to solve this for a sort of standard tree structure and HTML's <ul> and <li> elements.

    然而,这是一个预先排序的列表,因此不适合您的示例。另外,我一直想为一种标准树结构和HTML的

    • 元素解决这个问题。

    The basic concept I came up is the following:

    我提出的基本概念如下:

    1. TreeNode - Abstracts each element into a simple TreeNode type that can provide it's value (e.g. Name) and whether or not it has children.
    2. TreeNode - 将每个元素抽象为一个简单的TreeNode类型,该类型可以提供它的值(例如Name)以及它是否有子节点。

    3. TreeNodesIterator - A RecursiveIterator that is able to iterate over a set (array) of these TreeNodes. That is fairly simple as the TreeNode type already knows if it has children and which ones.
    4. TreeNodesIterator - 一个能够迭代这些TreeNode的集合(数组)的RecursiveIterator。这很简单,因为TreeNode类型已经知道它是否有子节点和哪些节点。

    5. RecursiveListIterator - A RecursiveIteratorIterator that has all the events needed when it recursively iterate over any kind of RecursiveIterator:
      • beginIteration / endIteration - Begin and end of the main list.
      • beginIteration / endIteration - 主列表的开头和结尾。

      • beginElement / endElement - Begin and end of each element.
      • beginElement / endElement - 每个元素的开头和结尾。

      • beginChildren / endChildren - Begin and end of each children list.This RecursiveListIterator only provides these events in a form of function calls. children lists, as it is typical for <ul><li> lists, are opened and closed inside it's parent <li> element. Therefore the endElement event is fired after the according endChildren event. This could be changed or made configurable to broaden the use this class. The events are distributed as function calls to a decorator object then, to keep things apart.
      • beginChildren / endChildren - 每个子列表的开头和结尾。此RecursiveListIterator仅以函数调用的形式提供这些事件。子列表,因为它是典型的

        • 列表,在其父
        • 元素内打开和关闭。因此,在endChildren事件之后触发endElement事件。这可以更改或可配置以扩大此类的使用范围。事件作为函数调用分发给装饰器对象,然后将事物分开。

    6. RecursiveListIterator - 一个RecursiveIteratorIterator,它具有递归迭代任何类型的RecursiveIterator时所需的所有事件:beginIteration / endIteration - 主列表的开始和结束。 beginElement / endElement - 每个元素的开头和结尾。 beginChildren / endChildren - 每个子列表的开头和结尾。此RecursiveListIterator仅以函数调用的形式提供这些事件。子列表,因为它是典型的

    • 列表,在其父
    • 元素内打开和关闭。因此,在endChildren事件之后触发endElement事件。这可以更改或可配置以扩大此类的使用范围。事件作为函数调用分发给装饰器对象,然后将事物分开。

  • ListDecorator - A "decorator" class that is just a receiver of the events of RecursiveListIterator.
  • ListDecorator - 一个“装饰器”类,它只是RecursiveListIterator事件的接收者。

    I start with the main output logic. Taken the now hierarchical $tree array, the final code looks like the following:

    我从主输出逻辑开始。采用现在分层的$ tree数组,最终代码如下所示:

    $root = new TreeNode($tree);$it = new TreeNodesIterator(array($root));$rit = new RecursiveListIterator($it);$decor = new ListDecorator($rit);$rit->addDecorator($decor);foreach($rit as $item){    $inset = $decor->inset(1);    printf("%s%s\n", $inset, $item->getName());}

    First let's look into the ListDecorator that simply wraps the <ul> and <li> elements and is deciding about how the list structure is output:

    首先让我们看一下ListDecorator,它简单地包装

    • 元素,并决定如何输出列表结构:

    class ListDecorator{    private $iterator;    public function __construct(RecursiveListIterator $iterator)    {        $this->iterator = $iterator;    }    public function inset($add = 0)    {        return str_repeat('  ', $this->iterator->getDepth()*2+$add);    }

    The constructor takes the list iterator it's working on. inset is just a helper function for nice indentation of the output. The rest are just the output functions for each event:

    构造函数接受它正在处理的列表迭代器。 inset只是一个帮助函数,可以很好地缩进输出。其余的只是每个事件的输出函数:

        public function beginElement()    {        printf("%s<li>\n", $this->inset());    }    public function endElement()    {        printf("%s</li>\n", $this->inset());    }    public function beginChildren()    {        printf("%s<ul>\n", $this->inset(-1));    }    public function endChildren()    {        printf("%s</ul>\n", $this->inset(-1));    }    public function beginIteration()    {        printf("%s<ul>\n", $this->inset());    }    public function endIteration()    {        printf("%s</ul>\n", $this->inset());    }}

    With these output functions in mind, this is the main output wrap-up / loop again, I go through it step by step:

    考虑到这些输出功能,这又是主要的输出总结/循环,我一步一步地完成它:

    $root = new TreeNode($tree);

    Create the root TreeNode which will be used to start iteration upon:

    创建将用于开始迭代的根TreeNode:

    $it = new TreeNodesIterator(array($root));

    This TreeNodesIterator is a RecursiveIterator that enables recursive iteration over the single $root node. It is passed as an array because that class needs something to iterate over and allows re-use with a set of children which is also an array of TreeNode elements.

    此TreeNodesIterator是一个RecursiveIterator,它允许在单个$ root节点上进行递归迭代。它作为一个数组传递,因为该类需要迭代一些东西并允许与一组子节点重用,这些子节点也是TreeNode元素的数组。

    $rit = new RecursiveListIterator($it);

    This RecursiveListIterator is a RecursiveIteratorIterator that provides the said events. To make use of it, only a ListDecorator needs to be provided (the class above) and assigned with addDecorator:

    此RecursiveListIterator是一个RecursiveIteratorIterator,它提供所述事件。要使用它,只需要提供ListDecorator(上面的类)并使用addDecorator分配:

    $decor = new ListDecorator($rit);$rit->addDecorator($decor);

    Then everything is set-up to just foreach over it and output each node:

    然后,所有内容都设置为只是对它进行预设并输出每个节点:

    foreach($rit as $item){    $inset = $decor->inset(1);    printf("%s%s\n", $inset, $item->getName());}

    As this example shows, the whole output logic is encapsulated in the ListDecorator class and this single foreach. The whole recursive traversal has been fully encapsulated into SPL recursive iterators which provided a stacked procedure, that means internally no recursion function calls are done.

    如此示例所示,整个输出逻辑封装在ListDecorator类和此单个foreach中。整个递归遍历已经完全封装到SPL递归迭代器中,它提供了一个堆栈过程,这意味着内部没有完成递归函数调用。

    The event based ListDecorator allows you to modify the output specifically and to provide multiple type of lists for the same data structure. It's even possible to change the input as the array data has been encapsulated into TreeNode.

    基于事件的ListDecorator允许您专门修改输出并为同一数据结构提供多种类型的列表。甚至可以在将数组数据封装到TreeNode中时更改输入。

    The full code example:

    完整的代码示例:

    <?phpnamespace My;$tree = array('H' => 'G', 'F' => 'G', 'G' => 'D', 'E' => 'D', 'A' => 'E', 'B' => 'C', 'C' => 'E', 'D' => null);// add children to parents$flat = array(); # temporary arrayforeach ($tree as $name => $parent){    $flat[$name]['name'] = $name; # self    if (NULL === $parent)    {        # no parent, is root element, assign it to $tree        $tree = &$flat[$name];    }    else    {        # has parent, add self as child            $flat[$parent]['children'][] = &$flat[$name];    }}unset($flat);class TreeNode{    protected $data;    public function __construct(array $element)    {        if (!isset($element['name']))            throw new InvalidArgumentException('Element has no name.');        if (isset($element['children']) && !is_array($element['children']))            throw new InvalidArgumentException('Element has invalid children.');        $this->data = $element;    }    public function getName()    {         return $this->data['name'];    }    public function hasChildren()    {        return isset($this->data['children']) && count($this->data['children']);    }    /**     * @return array of child TreeNode elements      */    public function getChildren()    {                $children = $this->hasChildren() ? $this->data['children'] : array();        $class = get_called_class();        foreach($children as &$element)        {            $element = new $class($element);        }        unset($element);                return $children;    }}class TreeNodesIterator implements \RecursiveIterator{    private $nodes;    public function __construct(array $nodes)    {        $this->nodes = new \ArrayIterator($nodes);    }    public function  getInnerIterator()    {        return $this->nodes;    }    public function getChildren()    {        return new TreeNodesIterator($this->nodes->current()->getChildren());    }    public function hasChildren()    {        return $this->nodes->current()->hasChildren();    }    public function rewind()    {        $this->nodes->rewind();    }    public function valid()    {        return $this->nodes->valid();    }       public function current()    {        return $this->nodes->current();    }    public function key()    {        return $this->nodes->key();    }    public function next()    {        return $this->nodes->next();    }}class RecursiveListIterator extends \RecursiveIteratorIterator{    private $elements;    /**     * @var ListDecorator     */    private $decorator;    public function addDecorator(ListDecorator $decorator)    {        $this->decorator = $decorator;    }    public function __construct($iterator, $mode = \RecursiveIteratorIterator::SELF_FIRST, $flags = 0)    {        parent::__construct($iterator, $mode, $flags);    }    private function event($name)    {        // event debug code: printf("--- %'.-20s --- (Depth: %d, Element: %d)\n", $name, $this->getDepth(), @$this->elements[$this->getDepth()]);        $callback = array($this->decorator, $name);        is_callable($callback) && call_user_func($callback);    }    public function beginElement()    {        $this->event('beginElement');    }    public function beginChildren()    {        $this->event('beginChildren');    }    public function endChildren()    {        $this->testEndElement();        $this->event('endChildren');    }    private function testEndElement($depthOffset = 0)    {        $depth = $this->getDepth() + $depthOffset;              isset($this->elements[$depth]) || $this->elements[$depth] = 0;        $this->elements[$depth] && $this->event('endElement');    }    public function nextElement()    {        $this->testEndElement();        $this->event('{nextElement}');        $this->event('beginElement');               $this->elements[$this->getDepth()] = 1;    }     public function beginIteration()    {        $this->event('beginIteration');    }    public function endIteration()    {        $this->testEndElement();        $this->event('endIteration');           }}class ListDecorator{    private $iterator;    public function __construct(RecursiveListIterator $iterator)    {        $this->iterator = $iterator;    }    public function inset($add = 0)    {        return str_repeat('  ', $this->iterator->getDepth()*2+$add);    }    public function beginElement()    {        printf("%s<li>\n", $this->inset(1));    }    public function endElement()    {        printf("%s</li>\n", $this->inset(1));    }    public function beginChildren()    {        printf("%s<ul>\n", $this->inset());    }    public function endChildren()    {        printf("%s</ul>\n", $this->inset());    }    public function beginIteration()    {        printf("%s<ul>\n", $this->inset());    }    public function endIteration()    {        printf("%s</ul>\n", $this->inset());    }}$root = new TreeNode($tree);$it = new TreeNodesIterator(array($root));$rit = new RecursiveListIterator($it);$decor = new ListDecorator($rit);$rit->addDecorator($decor);foreach($rit as $item){    $inset = $decor->inset(2);    printf("%s%s\n", $inset, $item->getName());}

    Outpupt:

    <ul>  <li>    D    <ul>      <li>        G        <ul>          <li>            H          </li>          <li>            F          </li>        </ul>      </li>      <li>        E        <ul>          </li>          <li>            A          </li>          <li>            C            <ul>              <li>                B              </li>            </ul>          </li>        </ul>      </li>    </ul>  </li></ul>

    Demo (PHP 5.2 variant)

    演示(PHP 5.2变体)

    A possible variant would be an iterator that iterates over any RecursiveIterator and provides an iteration over all events that can occur. An switch / case inside the foreach loop could then deal with the events.

    一个可能的变体是迭代器迭代任何RecursiveIterator并提供可能发生的所有事件的迭代。 foreach循环内的开关/案例可以处理事件。

    Related:

    #4


    Well, first I would turn the straight array of key-value pairs into a hierarchical array

    好吧,首先我将键值对的直接数组转换为分层数组

    function convertToHeiarchical(array $input) {    $parents = array();    $root = array();    $children = array();    foreach ($input as $item) {        $parents[$item['id']] = &$item;        if ($item['parent_id']) {            if (!isset($children[$item['parent_id']])) {                $children[$item['parent_id']] = array();            }            $children[$item['parent_id']][] = &$item;        } else {            $root = $item['id'];        }    }    foreach ($parents as $id => &$item) {        if (isset($children[$id])) {            $item['children'] = $children[$id];        } else {            $item['children'] = array();        }    }    return $parents[$root];}

    That'll can convert a flat array with parent_id and id into a hierarchical one:

    那可以将带有parent_id和id的平面数组转换为分层数组:

    $item = array(    'id' => 'A',    'blah' => 'blah',    'children' => array(        array(            'id' => 'B',            'blah' => 'blah',            'children' => array(                array(                    'id' => 'C',                    'blah' => 'blah',                    'children' => array(),                ),             ),            'id' => 'D',            'blah' => 'blah',            'children' => array(                array(                    'id' => 'E',                    'blah' => 'blah',                    'children' => array(),                ),            ),        ),    ),);

    Then, just create a rendering function:

    然后,只需创建一个渲染函数:

    function renderItem($item) {    $out = "Your OUtput For Each Item Here";    $out .= "<ul>";    foreach ($item['children'] as $child) {        $out .= "<li>".renderItem($child)."</li>";    }    $out .= "</ul>";    return $out;}

    #5


    While Alexander-Konstantinov's solution might not seem as easy to read at first, it is both genius and exponentially better in terms of performance, this should have been voted as the best answer.

    虽然亚历山大 - 康斯坦丁诺夫的解决方案起初可能看起来不那么容易,但就性能而言,它既是天才又是指数级的,这应该被认为是最好的答案。

    Thanks mate, I made a benchmark in your honor to compare the 2 solutions presented in this post.

    谢谢你的配偶,我为你的荣誉做了一个基准,比较了这篇文章中提出的两个解决方案。

    I had an @250k flat tree with 6 levels that I had to convert and I was searching for a better way to do so and avoid recursive iterations.

    我有一个@ 250k平面树,有6个级别,我必须转换,我正在寻找一个更好的方法这样做,并避免递归迭代。

    Recursion vs Reference:

    递归与参考:

    // Generate a 6 level flat tree$root = null;$lvl1 = 13;$lvl2 = 11;$lvl3 = 7;$lvl4 = 5;$lvl5 = 3;$lvl6 = 1;    $flatTree = [];for ($i = 1; $i <= 450000; $i++) {    if ($i % 3 == 0)  { $lvl5 = $i; $flatTree[$lvl6] = $lvl5; continue; }    if ($i % 5 == 0)  { $lvl4 = $i; $flatTree[$lvl5] = $lvl4; continue; }    if ($i % 7 == 0)  { $lvl3 = $i; $flatTree[$lvl3] = $lvl2; continue; }    if ($i % 11 == 0) { $lvl2 = $i; $flatTree[$lvl2] = $lvl1; continue; }    if ($i % 13 == 0) { $lvl1 = $i; $flatTree[$lvl1] = $root; continue; }    $lvl6 = $i;}echo 'Array count: ', count($flatTree), PHP_EOL;// Reference functionfunction treeByReference($flatTree){    $flat = [];    $tree = [];    foreach ($flatTree as $child => $parent) {        if (!isset($flat[$child])) {            $flat[$child] = [];        }        if (!empty($parent)) {            $flat[$parent][$child] =& $flat[$child];        } else {            $tree[$child] =& $flat[$child];        }    }    return $tree;}// Recursion functionfunction treeByRecursion($flatTree, $root = null){    $return = [];    foreach($flatTree as $child => $parent) {        if ($parent == $root) {            unset($flatTree[$child]);            $return[$child] = treeByRecursion($flatTree, $child);        }    }    return $return ?: [];}// Benchmark reference$t1 = microtime(true);$tree = treeByReference($flatTree);echo 'Reference: ', (microtime(true) - $t1), PHP_EOL;// Benchmark recursion$t2 = microtime(true);$tree = treeByRecursion($flatTree);echo 'Recursion: ', (microtime(true) - $t2), PHP_EOL;

    The output speaks for itself:

    输出不言自明:

    Array count: 255493Reference: 0.3259289264679 (less than 0.4s)Recursion: 6604.9865279198 (almost 2h)

    #6


    Well, to parse into ULs and LIs, it would be something like:

    那么,要解析为UL和LI,它将是这样的:

    $array = array (    'H' => 'G'    'F' => 'G'    'G' => 'D'    'E' => 'D'    'A' => 'E'    'B' => 'C'    'C' => 'E'    'D' => 'NULL');recurse_uls ($array, 'NULL');function recurse_uls ($array, $parent){    echo '<ul>';    foreach ($array as $c => $p)  {        if ($p != $parent) continue;        echo '<li>'.$c.'</li>';        recurse_uls ($array, $c);    }    echo '</ul>';}

    But I'd love to see a solution that doesn't require you to iterate through the array so often...

    但我很乐意看到一个解决方案,不需要你经常迭代数组......

    #7


    Here's what I came up with:

    这是我想出的:

    $arr = array(            'H' => 'G',            'F' => 'G',            'G' => 'D',            'E' => 'D',            'A' => 'E',            'B' => 'C',            'C' => 'E',            'D' => null );    $nested = parentChild($arr);    print_r($nested);    function parentChild(&$arr, $parent = false) {      if( !$parent) { //initial call         $rootKey = array_search( null, $arr);         return array($rootKey => parentChild($arr, $rootKey));      }else { // recursing through        $keys = array_keys($arr, $parent);        $piece = array();        if($keys) { // found children, so handle them          if( !is_array($keys) ) { // only one child            $piece = parentChild($arr, $keys);           }else{ // multiple children             foreach( $keys as $key ){               $piece[$key] = parentChild($arr, $key);             }           }        }else {           return $parent; //return the main tag (no kids)        }        return $piece; // return the array built via recursion      }    }

    outputs:

    Array(    [D] => Array        (            [G] => Array                (                    [H] => H                    [F] => F                )            [E] => Array                (                    [A] => A                    [C] => Array                        (                            [B] => B                        )                    )            )    )

    #8


    How to Create Dynamic Tree View and Menu

    如何创建动态树视图和菜单

    Step 1:First we will Create treeview table in mysql database. this table contains four column.id is the task id and name is the task name.

    第1步:首先我们将在mysql数据库中创建treeview表。此表包含四个column.id是任务ID,name是任务名称。

    --- Table structure for table `treeview_items`--CREATE TABLE IF NOT EXISTS `treeview_items` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(200) NOT NULL,  `title` varchar(200) NOT NULL,  `parent_id` varchar(11) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;---- Dumping data for table `treeview_items`--INSERT INTO `treeview_items` (`id`, `name`, `title`, `parent_id`) VALUES(1, 'task1', 'task1title', '2'),(2, 'task2', 'task2title', '0'),(3, 'task3', 'task1title3', '0'),(4, 'task4', 'task2title4', '3'),(5, 'task4', 'task1title4', '3'),(6, 'task5', 'task2title5', '5');

    Step 2: Tree view recursive methodI have created below tree createTreeView() method which call recursive if current task id is greater than prev task id.

    第2步:树视图递归方法我创建了下面的树createTreeView()方法,如果当前任务id大于prev任务id,则调用recursive。

    function createTreeView($array, $currentParent, $currLevel = 0, $prevLevel = -1) {foreach ($array as $categoryId => $category) {if ($currentParent == $category['parent_id']) {                           if ($currLevel > $prevLevel) echo " <ol class='tree'> ";     if ($currLevel == $prevLevel) echo " </li> ";    echo '<li> <label for="subfolder2">'.$category['name'].'</label> <input type="checkbox" name="subfolder2"/>';    if ($currLevel > $prevLevel) { $prevLevel = $currLevel; }    $currLevel++;     createTreeView ($array, $categoryId, $currLevel, $prevLevel);    $currLevel--;                   }   }if ($currLevel == $prevLevel) echo " </li>  </ol> ";}

    Step 3: Create index file to show tree view.This is main file of treeview example here we will call createTreeView() method with required parameters.

    步骤3:创建索引文件以显示树视图。这是树视图示例的主文件,我们将使用所需参数调用createTreeView()方法。

     <body><link rel="stylesheet" type="text/css" href="_styles.css" media="screen"><?phpmysql_connect('localhost', 'root');mysql_select_db('test');$qry="SELECT * FROM treeview_items";$result=mysql_query($qry);$arrayCategories = array();while($row = mysql_fetch_assoc($result)){  $arrayCategories[$row['id']] = array("parent_id" => $row['parent_id'], "name" =>                        $row['name']);     }?><div id="content" class="general-style1"><?phpif(mysql_num_rows($result)!=0){?><?php createTreeView($arrayCategories, 0); ?><?php}?></div></body>

    Step 4: Create CSS file style.cssHere we will write all css related class, currently I am using order list to create tree view. you can also change image path here.

    第4步:创建CSS文件style.cssHere我们将编写所有css相关的类,目前我正在使用order list创建树视图。你也可以在这里改变图像路径。

    img { border: none; }input, select, textarea, th, td { font-size: 1em; }/* CSS Tree menu styles */ol.tree{    padding: 0 0 0 30px;    width: 300px;}    li     {         position: relative;         margin-left: -15px;        list-style: none;    }    li.file    {        margin-left: -1px !important;    }        li.file a        {            background: url(document.png) 0 0 no-repeat;            color: #fff;            padding-left: 21px;            text-decoration: none;            display: block;        }        li.file a[href *= '.pdf']   { background: url(document.png) 0 0 no-repeat; }        li.file a[href *= '.html']  { background: url(document.png) 0 0 no-repeat; }        li.file a[href $= '.css']   { background: url(document.png) 0 0 no-repeat; }        li.file a[href $= '.js']        { background: url(document.png) 0 0 no-repeat; }    li input    {        position: absolute;        left: 0;        margin-left: 0;        opacity: 0;        z-index: 2;        cursor: pointer;        height: 1em;        width: 1em;        top: 0;    }        li input + ol        {            background: url(toggle-small-expand.png) 40px 0 no-repeat;            margin: -0.938em 0 0 -44px; /* 15px */            height: 1em;        }        li input + ol > li { display: none; margin-left: -14px !important; padding-left: 1px; }    li label    {        background: url(folder-horizontal.png) 15px 1px no-repeat;        cursor: pointer;        display: block;        padding-left: 37px;    }    li input:checked + ol    {        background: url(toggle-small.png) 40px 5px no-repeat;        margin: -1.25em 0 0 -44px; /* 20px */        padding: 1.563em 0 0 80px;        height: auto;    }        li input:checked + ol > li { display: block; margin: 0 0 0.125em;  /* 2px */}        li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ }

    More Details