Dies erfordert eine sehr einfache rekursive Funktion zum Parsen der untergeordneten / übergeordneten Paare in eine Baumstruktur und eine weitere rekursive Funktion zum Ausdrucken. Nur eine Funktion würde ausreichen, aber hier sind zwei zur Verdeutlichung (eine kombinierte Funktion finden Sie am Ende dieser Antwort).
Initialisieren Sie zuerst das Array der Kind / Eltern-Paare:
$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null
);
Dann die Funktion, die dieses Array in eine hierarchische Baumstruktur analysiert:
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;
}
Und eine Funktion, die diesen Baum durchläuft, um eine ungeordnete Liste auszudrucken:
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>';
}
}
Und die tatsächliche Verwendung:
$result = parseTree($tree);
printTree($result);
Hier ist der Inhalt von $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
)
)
)
)
)
)
)
)
Wenn Sie ein bisschen mehr Effizienz wünschen, können Sie diese Funktionen zu einer kombinieren und die Anzahl der durchgeführten Iterationen reduzieren:
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>';
}
}
Sie speichern nur 8 Iterationen in einem so kleinen Datensatz, aber in größeren Sätzen kann dies einen Unterschied machen.
Noch eine weitere Funktion zum Erstellen eines Baums (keine Rekursion erforderlich, verwendet stattdessen Referenzen):
Gibt ein hierarchisches Array wie dieses zurück:
Die mit rekursiver Funktion einfach als HTML-Liste gedruckt werden kann.
quelle
Eine andere, vereinfachte Möglichkeit, die flache Struktur
$tree
in eine Hierarchie umzuwandeln . Es wird nur ein temporäres Array benötigt, um es verfügbar zu machen:Das ist alles, um die Hierarchie in ein mehrdimensionales Array zu bringen:
Die Ausgabe ist weniger trivial, wenn Sie eine Rekursion vermeiden möchten (kann bei großen Strukturen eine Belastung sein).
Ich wollte immer das UL / LI- "Dilemma" für die Ausgabe eines Arrays lösen. Das Dilemma besteht darin, dass jedes Element nicht weiß, ob Kinder nachverfolgen oder wie viele vorhergehende Elemente geschlossen werden müssen. In einer anderen Antwort habe ich das bereits gelöst, indem ich eine
RecursiveIteratorIterator
und nachgetDepth()
anderen Metainformationen gesucht und gesucht habe , die ich selbst geschriebenIterator
habe: Verschachteltes Mengenmodell in einen<ul>
aber ausgeblendeten „geschlossenen“ Teilbaum zu bringen . Diese Antwort zeigt auch, dass Sie mit Iteratoren ziemlich flexibel sind.Dies war jedoch eine vorsortierte Liste und daher für Ihr Beispiel nicht geeignet. Außerdem wollte ich dies immer für eine Art Standardbaumstruktur sowie HTMLs
<ul>
und<li>
Elemente lösen .Das Grundkonzept, das ich mir ausgedacht habe, ist das folgende:
TreeNode
- Abstrakt jedes Element in einen einfachenTreeNode
Typ, der seinen Wert liefern kann (z. B.Name
) und ob es untergeordnete Elemente hat oder nicht.TreeNodesIterator
- ARecursiveIterator
, das über eine Menge (Array) von diesen iterieren kannTreeNodes
. Das ist ziemlich einfach, da derTreeNode
Typ bereits weiß, ob und welche Kinder er hat.RecursiveListIterator
- ARecursiveIteratorIterator
, das alle Ereignisse enthält, die erforderlich sind, wenn es rekursiv über eine der folgenden Arten iteriertRecursiveIterator
:beginIteration
/endIteration
- Anfang und Ende der Hauptliste.beginElement
/endElement
- Anfang und Ende jedes Elements.beginChildren
/endChildren
- Anfang und Ende jeder Kinderliste. DiesRecursiveListIterator
stellt diese Ereignisse nur in Form von Funktionsaufrufen bereit. Untergeordnete Listen werden, wie es für<ul><li>
Listen typisch ist , innerhalb des übergeordneten<li>
Elements geöffnet und geschlossen . Daher wird dasendElement
Ereignis nach dem entsprechendenendChildren
Ereignis ausgelöst . Dies kann geändert oder konfigurierbar gemacht werden, um die Verwendung dieser Klasse zu erweitern. Die Ereignisse werden dann als Funktionsaufrufe an ein Dekorationsobjekt verteilt, um die Dinge auseinander zu halten.ListDecorator
- Eine "Dekorateur" -Klasse, die nur ein Empfänger der Ereignisse von istRecursiveListIterator
.Ich beginne mit der Hauptausgangslogik. In dem jetzt hierarchischen
$tree
Array sieht der endgültige Code wie folgt aus:Lassen Sie uns zunächst Blick in die ,
ListDecorator
die einfach wickelt die<ul>
und<li>
Elemente und ist die Entscheidung darüber , wie die Listenstruktur ausgegeben:Der Konstruktor verwendet den Listeniterator, an dem er arbeitet.
inset
ist nur eine Hilfsfunktion für eine schöne Einrückung der Ausgabe. Der Rest sind nur die Ausgabefunktionen für jedes Ereignis:In Anbetracht dieser Ausgabefunktionen ist dies wieder die Hauptausgabe-Zusammenfassung / Schleife. Ich gehe sie Schritt für Schritt durch:
Erstellen Sie die Wurzel
TreeNode
, mit der die Iteration gestartet wird:Dies
TreeNodesIterator
ist eine ,RecursiveIterator
die rekursive Iteration über den einzelnen ermöglicht$root
Knoten. Es wird als Array übergeben, da diese Klasse etwas zum Durchlaufen benötigt und die Wiederverwendung mit einer Reihe von untergeordnetenTreeNode
Elementen ermöglicht, die auch ein Array von Elementen sind.Dies
RecursiveListIterator
ist eineRecursiveIteratorIterator
, die die genannten Ereignisse bereitstellt. Um davon Gebrauch zu machen, muss nur einListDecorator
(die obige Klasse) bereitgestellt und zugewiesen werden mitaddDecorator
:Dann wird alles so eingestellt, dass es direkt
foreach
darüber liegt und jeden Knoten ausgibt:Wie dieses Beispiel zeigt, ist die gesamte Ausgabelogik in der
ListDecorator
Klasse und dieser einzelnen gekapseltforeach
. Die gesamte rekursive Durchquerung wurde vollständig in rekursive SPL-Iteratoren eingekapselt, die eine gestapelte Prozedur bereitstellten. Dies bedeutet, dass intern keine Rekursionsfunktionsaufrufe ausgeführt werden.Mit der ereignisbasierten Funktion
ListDecorator
können Sie die Ausgabe spezifisch ändern und mehrere Listentypen für dieselbe Datenstruktur bereitstellen. Es ist sogar möglich, die Eingabe zu ändern, wenn die Array-Daten eingekapselt wurdenTreeNode
.Das vollständige Codebeispiel:
Ausbruch:
Demo (PHP 5.2 Variante)
Eine mögliche Variante wäre ein Iterator, der
RecursiveIterator
über alle Ereignisse iteriert und eine Iteration über alle Ereignisse bereitstellt, die auftreten können. Ein Schalter / Fall innerhalb der foreach-Schleife könnte sich dann mit den Ereignissen befassen.Verbunden:
quelle
Nun, zuerst würde ich das gerade Array von Schlüssel-Wert-Paaren in ein hierarchisches Array verwandeln
Dadurch kann ein flaches Array mit parent_id und id in ein hierarchisches Array konvertiert werden:
Erstellen Sie dann einfach eine Rendering-Funktion:
quelle
Während die Lösung von Alexander-Konstantinov auf den ersten Blick nicht so einfach zu lesen scheint, ist sie sowohl genial als auch exponentiell besser in Bezug auf die Leistung. Dies hätte als beste Antwort gewählt werden müssen.
Vielen Dank, Kumpel, ich habe zu Ihren Ehren einen Benchmark erstellt, um die beiden in diesem Beitrag vorgestellten Lösungen zu vergleichen.
Ich hatte einen @ 250k flachen Baum mit 6 Ebenen, die ich konvertieren musste, und suchte nach einem besseren Weg, dies zu tun und rekursive Iterationen zu vermeiden.
Rekursion vs Referenz:
Die Ausgabe spricht für sich:
quelle
Nun, um ULs und LIs zu analysieren, wäre es ungefähr so:
Aber ich würde gerne eine Lösung sehen, bei der Sie nicht so oft durch das Array iterieren müssen ...
quelle
Folgendes habe ich mir ausgedacht:
Ausgänge:
quelle
Verschachteltes Array der Eltern-Kind-Beziehung
Ruft den gesamten Datensatz aus der Datenbank ab und erstellt ein verschachteltes Array.
Drucken Sie Daten zu Kategorien und Unterkategorien im JSON-Format
quelle
$ aa = $ this-> parseTree ($ tree);
quelle
Alte Frage, aber auch ich musste dies tun und die Beispiele mit Rekursion bereiteten mir Kopfschmerzen. In meiner Datenbank haben wir eine
locations
Tabelle, die eineloca_id
PK (Kind) und eine selbstreferenzierendeloca_parent_id
(Eltern) war.Ziel ist es, diese Struktur in HTML darzustellen. Die einfache Abfrage kann die Daten in fester Reihenfolge zurückgeben, aber ich fand sie nicht gut genug, um solche Daten auf natürliche Weise anzuzeigen. Was ich wirklich wollte, war die Bearbeitung von Oracle Tree Walks
LEVEL
, um die Anzeige zu erleichtern.Ich entschied mich für die Idee eines "Pfades", um jeden Eintrag eindeutig zu identifizieren. Beispielsweise:
Das Sortieren des Arrays nach dem Pfad sollte die Verarbeitung für eine aussagekräftige Anzeige erleichtern.
Mir ist klar, dass die Verwendung von assoziativen Arrays und Sortierungen betrügt, da sie die rekursive Komplexität der Operationen verbirgt, aber für mich sieht dies einfacher aus:
quelle
So erstellen Sie eine dynamische Baumansicht und ein Menü
Schritt 1: Zuerst erstellen wir eine Baumansichtstabelle in der MySQL-Datenbank. Diese Tabelle enthält vier Spalten. ID ist die Aufgaben-ID und Name ist der Aufgabenname.
Schritt 2: Rekursive Methode für die Baumansicht Ich habe unten die Methode createTreeView () für den Baum erstellt, die rekursiv aufruft, wenn die aktuelle Aufgaben-ID größer als die vorherige Aufgaben-ID ist.
Schritt 3: Erstellen Sie eine Indexdatei, um die Baumansicht anzuzeigen. Dies ist die Hauptdatei des Treeview-Beispiels. Hier rufen wir die Methode createTreeView () mit den erforderlichen Parametern auf.
Schritt 4: Erstellen der CSS-Datei style.css Hier schreiben wir alle CSS-bezogenen Klassen. Derzeit verwende ich die Bestellliste, um eine Baumansicht zu erstellen. Hier können Sie auch den Bildpfad ändern.
Mehr Details
quelle