php 将一系列父子关系转化为层次树?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2915748/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert a series of parent-child relationships into a hierarchical tree?
提问by Eric
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.
我想要的最终结果是一组嵌套的<ul>元素,每个元素都<li>包含孩子的名字。
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 对的数组转到一组 Nested <ul>?
I have a feeling that recursion is involved, but I'm not quite awake enough to think it through.
我有一种涉及递归的感觉,但我还不够清醒,无法仔细考虑。
回答by Tatu Ulmanen
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 次迭代,但在更大的数据集上它可能会有所作为。
回答by Alexander Konstantinov
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 列表。
回答by hakre
Another, more simplified way to convert the flat structure in the $treeinto a hierarchy. Only one temporary array is needed to expose it:
另一种更简化的方法将 中的平面结构$tree转换为层次结构。只需要一个临时数组来暴露它:
// add children to parents
$flat = array(); # temporary array
foreach ($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 RecursiveIteratorIteratorand looking for getDepth()and other meta-information that my own written Iteratorprovided: Getting nested set model into a <ul>but hiding “closed” subtrees. That answershows as well that with iterators you're quite flexible.
我一直想解决 UL/LI 输出数组的“困境”。困境在于,每个项目不知道子项是否会跟进或需要关闭多少前面的元素。在另一个答案中,我已经通过使用 aRecursiveIteratorIterator并寻找getDepth()我自己编写的其他元信息来解决这个问题Iterator:将嵌套集模型放入一个<ul>但隐藏“关闭”子树。该答案还表明,使用迭代器您非常灵活。
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<ul>和<li>元素解决这个问题。
The basic concept I came up is the following:
我提出的基本概念如下:
TreeNode- Abstracts each element into a simpleTreeNodetype that can provide it's value (e.g.Name) and whether or not it has children.TreeNodesIterator- ARecursiveIteratorthat is able to iterate over a set (array) of theseTreeNodes. That is fairly simple as theTreeNodetype already knows if it has children and which ones.RecursiveListIterator- ARecursiveIteratorIteratorthat has all the events needed when it recursively iterate over any kind ofRecursiveIterator:beginIteration/endIteration- Begin and end of the main list.beginElement/endElement- Begin and end of each element.beginChildren/endChildren- Begin and end of each children list. ThisRecursiveListIteratoronly 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 theendElementevent is fired after the accordingendChildrenevent. 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.
ListDecorator- A "decorator" class that is just a receiver of the events ofRecursiveListIterator.
TreeNode- 将每个元素抽象为一个简单的TreeNode类型,可以提供它的值(例如Name)以及它是否有子元素。TreeNodesIterator-RecursiveIterator能够迭代这些TreeNodes. 这相当简单,因为TreeNode类型已经知道它是否有孩子以及哪些孩子。RecursiveListIterator- ARecursiveIteratorIterator具有递归迭代任何类型时所需的所有事件RecursiveIterator:beginIteration/endIteration- 主列表的开始和结束。beginElement/endElement- 每个元素的开始和结束。beginChildren/endChildren- 每个子列表的开始和结束。这RecursiveListIterator仅以函数调用的形式提供这些事件。子列表,因为它是典型的<ul><li>列表,在其父<li>元素内打开和关闭。因此,endElement在相应endChildren事件之后触发该事件。这可以更改或进行配置以扩大此类的使用。然后将事件作为对装饰器对象的函数调用分发,以将事情分开。
ListDecorator- 一个“装饰器”类,它只是RecursiveListIterator.
I start with the main output logic. Taken the now hierarchical $treearray, 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 ListDecoratorthat simply wraps the <ul>and <li>elements and is deciding about how the list structure is output:
首先让我们看看ListDecorator简单地包装<ul>和<li>元素并决定如何输出列表结构的 :
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. insetis 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 TreeNodewhich will be used to start iteration upon:
创建TreeNode将用于开始迭代的根:
$it = new TreeNodesIterator(array($root));
This TreeNodesIteratoris a RecursiveIteratorthat enables recursive iteration over the single $rootnode. 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 TreeNodeelements.
这TreeNodesIterator是一个RecursiveIterator可以在单个$root节点上进行递归迭代的方法。它作为一个数组传递,因为该类需要一些东西来迭代并允许与一组子TreeNode元素重用,这些子元素也是一个元素数组。
$rit = new RecursiveListIterator($it);
This RecursiveListIteratoris a RecursiveIteratorIteratorthat provides the said events. To make use of it, only a ListDecoratorneeds 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 foreachover it and output each node:
然后一切都设置foreach在它上面并输出每个节点:
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 ListDecoratorclass 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 ListDecoratorallows 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:
完整代码示例:
<?php
namespace 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 array
foreach ($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>
A possible variant would be an iterator that iterates over any RecursiveIteratorand provides an iteration over all events that can occur. An switch / case inside the foreach loop could then deal with the events.
一个可能的变体是一个迭代器,它对任何RecursiveIterator事件进行迭代并提供对所有可能发生的事件的迭代。foreach 循环中的 switch / case 然后可以处理事件。
Related:
有关的:
回答by ircmaxell
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;
}
回答by ntt
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.
虽然Alexander-Konstantinov的解决方案一开始可能看起来并不容易阅读,但它既是天才又是性能方面的指数级更好,这应该被选为最佳答案。
Thanks mate, I made a benchmark in your honor to compare the 2 solutions presented in this post.
谢谢伙计,为了您的荣誉,我做了一个基准来比较这篇文章中提出的 2 个解决方案。
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 function
function 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 function
function 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: 255493
Reference: 0.3259289264679 (less than 0.4s)
Recursion: 6604.9865279198 (almost 2h)
回答by Dan Heberden
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
)
)
)
)
回答by arnorhs
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...
但我很想看到一个不需要你经常遍历数组的解决方案......
回答by Akshay Mistry
Parent-child relationship nested Array
Fetch all the record from the database and creating nested array.
父子关系嵌套数组
从数据库中获取所有记录并创建嵌套数组。
$data = SampleTable::find()->all();
$tree = buildTree($data);
print_r($tree);
public function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element['iParentId'] == $parentId) {
$children =buildTree($elements, $element['iCategoriesId']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
Print Categories and Sub-categories data in json Format
以json格式打印类别和子类别数据
public static function buildTree(array $elements, $parentId = 0){
$branch = array();
foreach($elements as $element){
if($element['iParentId']==$parentId){
$children =buildTree($elements, $element['iCategoriesId']);
if ($children) {
$element['children'] = $children;
}
$branch[] = array(
'iCategoriesId' => $element->iCategoriesId,
'iParentId'=>$element->iParentId,
'vCategoriesName'=>$element->vCategoriesName,
'children'=>$element->children,
);
}
}
return[
$branch
];
}
回答by Bharat
$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null,
'Z' => null,
'MM' =>'Z',
'KK' =>'Z',
'MMM' =>'MM',
// 'MM'=>'DDD'
);
$aa=$this->parseTree($tree);
$aa=$this->parseTree($tree);
public function get_tress($tree,$key)
{
$x=array();
foreach ($tree as $keys => $value) {
if($value==$key){
$x[]=($keys);
}
}
echo "<li>";
foreach ($x as $ke => $val) {
echo "<ul>";
echo($val);
$this->get_tress($tree,$val);
echo "</ul>";
}
echo "</li>";
}
function parseTree($tree, $root = null) {
foreach ($tree as $key => $value) {
if($value==$root){
echo "<ul>";
echo($key);
$this->get_tress($tree,$key);
echo "</ul>";
}
}
回答by Bharat
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表。该表包含四列。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 method I have created below tree createTreeView() method which call recursive if current task id is greater than prev task id.
第 2 步:树视图递归方法我在树 createTreeView() 方法下创建,如果当前任务 id 大于上一个任务 id,则调用递归。
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">
<?php
mysql_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">
<?php
if(mysql_num_rows($result)!=0)
{
?>
<?php
createTreeView($arrayCategories, 0); ?>
<?php
}
?>
</div>
</body>
Step 4: Create CSS file style.css Here 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.css 这里我们将编写所有与CSS相关的类,目前我使用订单列表来创建树视图。您也可以在此处更改图像路径。
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 */ }

