将一系列父子关系转化为层次树?
我有一堆名称-父名称对,我想将它们变成尽可能少的层次树结构。例如,这些可能是配对:
Child : Parent
H : G
F : G
G : D
E : D
A : E
B : C
C : E
D : NULL
需要将其转换为(一)个层次树:
D
├── E
│ ├── A
│ │ └── B
│ └── C
└── G
├── F
└── H
我想要的最终结果是一组嵌套的
包含孩子的名字。
元素,其中每个
配对中没有不一致的地方(孩子是它自己的父母,父母是孩子的孩子,等等),因此可能可以进行一系列优化。
在 PHP 中,我如何从包含 child=>parent 对的数组转换为一组嵌套
?我有一种感觉,其中涉及到递归,但我还不够清醒,无法思考清楚。
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):
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?
I have a feeling that recursion is involved, but I'm not quite awake enough to think it through.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
这需要一个非常基本的递归函数将子/父对解析为树结构,并需要另一个递归函数将其打印出来。只需一个函数就足够了,但为了清楚起见,这里有两个(可以在本答案的末尾找到组合函数)。
首先初始化子/父对数组:
然后将该数组解析为分层树结构的函数:
以及遍历该树以打印出无序列表的函数:
实际用法:
这是
$ 的内容结果
:如果你想要更高一点的效率,你可以将这些函数合并为一个,并减少迭代次数:
在如此小的数据集上,你只能保存 8 次迭代,但在更大的数据集上,它可以使一个区别。
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:
Then the function that parses that array into a hierarchical tree structure:
And a function that traverses that tree to print out an unordered list:
And the actual usage:
Here's the contents of
$result
:If you want a bit more efficiency, you can combine those functions into one and reduce the number of iterations made:
You'll only save 8 iterations on a dataset as small as this but on bigger sets it could make a difference.
另一个制作树的函数(不涉及递归,而是使用引用):
返回一个像这样的分层数组:
可以使用递归函数轻松地将其打印为 HTML 列表。
Yet Another Function To Make A Tree (no recursion involved, uses references instead):
Returns an hierarchical array like this one:
Which can easily be printed as a HTML list using recursive function.
另一种更简单的方法是将
$tree
中的平面结构转换为层次结构。只需要一个临时数组来公开它:这就是将层次结构转换为多维数组的全部内容:
如果您想避免递归(可能成为大型结构的负担),则输出就不那么简单了。
我一直想解决输出数组的 UL/LI“困境”。困境是,每个项目不知道孩子是否会跟进,或者需要关闭多少个前面的元素。在另一个答案中,我已经通过使用 RecursiveIteratorIterator 并查找 getDepth() 和我自己编写的 Iterator 提供的其他元信息解决了这个问题: 将嵌套集合模型放入
但隐藏“闭合”子树。这个答案也表明使用迭代器你非常灵活。
然而,这是一个预先排序的列表,因此不适合您的示例。此外,我一直想通过某种标准树结构和 HTML 的
和
元素来解决这个问题。
我提出的基本概念如下:
TreeNode
- 将每个元素抽象为一个简单的TreeNode
类型,该类型可以提供其值(例如>名称
)以及它是否有子项。TreeNodesIterator
- 一个RecursiveIterator
,能够迭代这些TreeNodes
的集合(数组)。这相当简单,因为TreeNode
类型已经知道它是否有子节点以及哪些子节点。RecursiveListIterator
- 一个RecursiveIteratorIterator
,它具有递归迭代任何类型的RecursiveIterator
时所需的所有事件:beginIteration
/endIteration
- 主列表的开始和结束。beginElement
/endElement
- 每个元素的开始和结束。beginChildren
/endChildren
- 每个子列表的开始和结束。这个RecursiveListIterator仅以函数调用的形式提供这些事件。子列表(如
列表的典型特征)在其父
endElement
事件在相应的endChildren
事件之后触发。这可以更改或配置以扩大此类的使用范围。然后,事件作为对装饰器对象的函数调用进行分发,以将事物分开。ListDecorator
- 一个“装饰器”类,它只是RecursiveListIterator
事件的接收者。我从主要输出逻辑开始。采用现在分层的
$tree
数组,最终代码如下所示:首先让我们看看简单包装
的
ListDecorator
> 和元素,并决定如何输出列表结构:
构造函数采用它正在处理的列表迭代器。
inset
只是一个用于对输出进行良好缩进的辅助函数。其余的只是每个事件的输出函数:考虑到这些输出函数,这又是主要输出总结/循环,我将逐步完成它:
创建根
TreeNode
,其中将用于启动迭代:此
TreeNodesIterator
是一个RecursiveIterator
,可在单个$root
节点上进行递归迭代。它作为数组传递,因为该类需要迭代某些内容,并允许与一组子项重用,这些子项也是TreeNode
元素的数组。这个RecursiveListIterator是一个提供上述事件的RecursiveIteratorIterator。要使用它,只需要提供
ListDecorator
(上面的类)并使用addDecorator
进行分配:然后所有内容都设置为
foreach< /code> 并输出每个节点:
如本例所示,整个输出逻辑封装在
ListDecorator
类和这个foreach
中。整个递归遍历已完全封装到 SPL 递归迭代器中,该迭代器提供了堆栈过程,这意味着内部不执行递归函数调用。基于事件的
ListDecorator
允许您专门修改输出并为同一数据结构提供多种类型的列表。甚至可以更改输入,因为数组数据已封装到 TreeNode 中。完整的代码示例:
输出:
演示(PHP 5.2 变体)
一个可能的变体是一个迭代器,可以迭代任何
RecursiveIterator
并提供对所有可能发生的事件的迭代。然后,foreach 循环内的 switch/case 可以处理事件。相关:
Another, more simplified way to convert the flat structure in the
$tree
into a hierarchy. Only one temporary array is needed to expose it:That's all for getting the hierarchy into a multidimensional array:
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 forgetDepth()
and other meta-information that my own writtenIterator
provided: Getting nested set model into a<ul>
but hiding “closed” subtrees. That answer shows as well that with iterators you're quite flexible.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.The basic concept I came up is the following:
TreeNode
- Abstracts each element into a simpleTreeNode
type that can provide it's value (e.g.Name
) and whether or not it has children.TreeNodesIterator
- ARecursiveIterator
that is able to iterate over a set (array) of theseTreeNodes
. That is fairly simple as theTreeNode
type already knows if it has children and which ones.RecursiveListIterator
- ARecursiveIteratorIterator
that 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.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 theendElement
event is fired after the accordingendChildren
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.ListDecorator
- A "decorator" class that is just a receiver of the events ofRecursiveListIterator
.I start with the main output logic. Taken the now hierarchical
$tree
array, the final code looks like the following: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: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:With these output functions in mind, this is the main output wrap-up / loop again, I go through it step by step:
Create the root
TreeNode
which will be used to start iteration upon:This
TreeNodesIterator
is aRecursiveIterator
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 ofTreeNode
elements.This
RecursiveListIterator
is aRecursiveIteratorIterator
that provides the said events. To make use of it, only aListDecorator
needs to be provided (the class above) and assigned withaddDecorator
:Then everything is set-up to just
foreach
over it and output each node:As this example shows, the whole output logic is encapsulated in the
ListDecorator
class and this singleforeach
. 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.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 intoTreeNode
.The full code example:
Outpupt:
Demo (PHP 5.2 variant)
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.Related:
虽然 Alexander-Konstantinov 的解决方案乍一看可能不那么容易阅读,但它既天才又好得多就性能而言,这应该被选为最佳答案。
谢谢伙计,为了您的荣幸,我做了一个基准测试来比较本文中提出的两种解决方案。
我有一个 @250k 扁平树,有 6 个级别,我必须对其进行转换,并且我正在寻找一种更好的方法来执行此操作并避免递归迭代。
递归与引用:
输出不言而喻:
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.
Recursion vs Reference:
The output speaks for itself:
好吧,首先我会将键值对的直接数组转换为分层数组,
这可以将带有parent_id和id的平面数组转换为分层数组:
然后,只需创建一个渲染函数:
Well, first I would turn the straight array of key-value pairs into a hierarchical array
That'll can convert a flat array with parent_id and id into a hierarchical one:
Then, just create a rendering function:
好吧,要解析为 UL 和 LI,它会是这样的:
但我很乐意看到一个不需要您如此频繁地迭代数组的解决方案......
Well, to parse into ULs and LIs, it would be something like:
But I'd love to see a solution that doesn't require you to iterate through the array so often...
这是我想出的:
输出:
Here's what I came up with:
outputs:
父子关系嵌套数组
从数据库中获取所有记录并创建嵌套数组。
以 json 格式打印类别和子类别数据
Parent-child relationship nested Array
Fetch all the record from the database and creating nested array.
Print Categories and Sub-categories data in json Format
$aa=$this->parseTree($tree);
$aa=$this->parseTree($tree);
老问题,但我也必须这样做,并且递归的例子让我头疼。在我的数据库中,我们有一个
locations
表,它是一个loca_id
PK(子级)和自引用loca_parent_id
(父级)。目的是用 HTML 表示此结构。简单的查询可以返回一些固定顺序的数据,但我发现不足以以自然的方式显示这些数据。我真正想要的是使用
LEVEL
处理 Oracle 树遍历来帮助显示。我决定使用“路径”的想法来唯一标识每个条目。例如:
按路径对数组进行排序应该可以更轻松地处理有意义的显示。
我意识到使用关联数组和排序是作弊的,因为它隐藏了操作的递归复杂性,但对我来说这看起来更简单:
Old question, but I too had to do this and the examples with recursion gave me a headache. In my database we have a
locations
table, which was aloca_id
PK (Child) and self referencingloca_parent_id
(Parent).The aim is to represent this structure in HTML. The simple query can of couyrse return the data is some fixed order but I found not well enough to display such data in a natural way. What I really wanted was Oracle tree walk handling with
LEVEL
to help with display.I decided to use the idea of a 'path' to uniquely identify each entry. For example:
Sorting the array by the path should make it easier to process for meaningful display.
I realise that use of associative arrays and sorts is cheating as it hides the recursive complexity of the operations, but to me this look simpler:
如何创建动态树视图和菜单
第1步:首先我们将在mysql数据库中创建树视图表。该表包含四列。id 是任务 id,name 是任务名称。
步骤2:树视图递归方法
我创建了下面的树 createTreeView() 方法,如果当前任务 id 大于上一个任务 id,则该方法调用递归。
步骤3:创建索引文件以显示树视图。
这是树视图示例的主文件,我们将使用所需参数调用 createTreeView() 方法。
步骤4:创建CSS文件style.css
在这里我们将编写所有与CSS相关的类,目前我正在使用订单列表来创建树视图。您还可以在此处更改图像路径。
更多详情
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.
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.
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.
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.
More Details