Wymaga to bardzo podstawowej funkcji rekurencyjnej do przeanalizowania par dziecko / rodzic do struktury drzewa i innej funkcji rekurencyjnej do jej wydrukowania. Wystarczyłaby tylko jedna funkcja, ale dla jasności są dwie (połączoną funkcję można znaleźć na końcu tej odpowiedzi).
Najpierw zainicjalizuj tablicę par dziecko / rodzic:
$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null
);
Następnie funkcja, która analizuje tę tablicę w hierarchiczną strukturę drzewa:
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;
}
I funkcja, która przechodzi przez to drzewo, aby wydrukować nieuporządkowaną 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>';
}
}
A rzeczywiste użycie:
$result = parseTree($tree);
printTree($result);
Oto zawartość $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
)
)
)
)
)
)
)
)
Jeśli chcesz nieco większej wydajności, możesz połączyć te funkcje w jedną i zmniejszyć liczbę wykonywanych iteracji:
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>';
}
}
Zaoszczędzisz tylko 8 iteracji na tak małym zbiorze danych, ale w przypadku większych zestawów może to mieć znaczenie.
Jeszcze inna funkcja do tworzenia drzewa (bez rekursji, zamiast tego używa odwołań):
Zwraca hierarchiczną tablicę, taką jak ta:
Które można łatwo wydrukować jako listę HTML za pomocą funkcji rekurencyjnej.
źródło
Kolejny, bardziej uproszczony sposób na przekształcenie płaskiej struktury w
$tree
hierarchię. Do jej udostępnienia potrzebna jest tylko jedna tymczasowa tablica:To wszystko, aby uzyskać hierarchię w wielowymiarowej tablicy:
Wynik jest mniej trywialny, jeśli chcesz uniknąć rekursji (może być obciążeniem przy dużych strukturach).
Zawsze chciałem rozwiązać „dylemat” UL / LI dotyczący wyprowadzania tablicy. Dylemat polega na tym, że każda pozycja nie wie, czy dzieci będą kontynuowały lub ile elementów poprzedzających należy zamknąć. W innej odpowiedzi już to rozwiązałem, używając
RecursiveIteratorIterator
szukaniagetDepth()
a i innych metainformacji, które dostarczył mój własnyIterator
: Przekształcanie zagnieżdżonego modelu zbioru w<ul>
ale ukrywające „zamknięte” poddrzewa . Ta odpowiedź pokazuje również, że dzięki iteratorom jesteś dość elastyczny.Jednak była to wstępnie posortowana lista, więc nie byłaby odpowiednia dla twojego przykładu. Dodatkowo zawsze chciałem rozwiązać to dla pewnego rodzaju standardowej struktury drzewa oraz HTML
<ul>
i<li>
elementów.Podstawowa koncepcja, którą wymyśliłem, jest następująca:
TreeNode
- Abstrahuje każdy element do prostegoTreeNode
typu, który może zapewnić jego wartość (np.Name
) I określa, czy ma on dzieci.TreeNodesIterator
- ARecursiveIterator
która jest w stanie wykonać iterację po zestawie (tablicy) tychTreeNodes
. Jest to dość proste, ponieważTreeNode
typ już wie, czy ma dzieci i które z nich.RecursiveListIterator
- A,RecursiveIteratorIterator
która zawiera wszystkie potrzebne zdarzenia, gdy rekurencyjnie wykonuje iterację dowolnego rodzajuRecursiveIterator
:beginIteration
/endIteration
- Początek i koniec głównej listy.beginElement
/endElement
- Początek i koniec każdego elementu.beginChildren
/endChildren
- Początek i koniec każdej listy dzieci. Zapewnia toRecursiveListIterator
tylko te zdarzenia w postaci wywołań funkcji. listy dzieci, jak to jest typowe dla<ul><li>
list, są otwierane i zamykane wewnątrz<li>
elementu nadrzędnego . DlategoendElement
zdarzenie jest uruchamiane po odpowiednimendChildren
zdarzeniu. Można to zmienić lub skonfigurować w celu rozszerzenia zastosowania tej klasy. Zdarzenia są następnie dystrybuowane jako wywołania funkcji do obiektu dekoratora, aby oddzielić rzeczy.ListDecorator
- Klasa „dekorator” będąca tylko odbiorcą wydarzeń zRecursiveListIterator
.Zacznę od głównej logiki wyjściowej. Biorąc pod uwagę hierarchiczną
$tree
tablicę now , ostateczny kod wygląda następująco:Wygląd Zacznijmy Into the
ListDecorator
że po prostu opakowuje<ul>
i<li>
elementy i decyduje o tym, jak struktura lista jest wyjście:Konstruktor pobiera iterator listy, nad którym pracuje.
inset
jest tylko funkcją pomocniczą do ładnego wcięcia danych wyjściowych. Reszta to tylko funkcje wyjściowe dla każdego zdarzenia:Mając na uwadze te funkcje wyjściowe, jest to ponownie główne zawijanie / pętla wyjścia, przechodzę przez to krok po kroku:
Utwórz root,
TreeNode
który będzie używany do rozpoczęcia iteracji po:To
TreeNodesIterator
jest,RecursiveIterator
które umożliwia rekurencyjną iterację w pojedynczym$root
węźle. Jest przekazywana jako tablica, ponieważ ta klasa wymaga czegoś do iteracji i umożliwia ponowne użycie z zestawem elementów potomnych, który jest również tablicąTreeNode
elementów.To
RecursiveListIterator
jest,RecursiveIteratorIterator
które zapewnia wspomniane zdarzenia. Aby z niego skorzystać, wystarczyListDecorator
podać (klasę powyżej) i przypisaćaddDecorator
:Następnie wszystko jest ustawione tak, aby tuż
foreach
nad nim i wyprowadzało każdy węzeł:Jak pokazuje ten przykład, cała logika wyjściowa jest zawarta w
ListDecorator
klasie i tym pojedynczymforeach
. Całe przechodzenie rekurencyjne zostało w pełni zahermetyzowane w iteratorach rekurencyjnych SPL, które zapewniały procedurę stosową, co oznacza, że wewnętrznie nie są wykonywane żadne wywołania funkcji rekurencyjnych.Oparta na zdarzeniach
ListDecorator
umożliwia konkretną modyfikację danych wyjściowych i zapewnienie wielu typów list dla tej samej struktury danych. Można nawet zmienić dane wejściowe, gdy dane tablicy zostały hermetyzowaneTreeNode
.Przykład pełnego kodu:
Outpupt:
Demo (wariant PHP 5.2)
Możliwym wariantem byłby iterator, który iteruje po każdym
RecursiveIterator
i zapewnia iterację po wszystkich zdarzeniach, które mogą wystąpić. Przełącznik / przypadek wewnątrz pętli foreach mógłby wtedy obsługiwać zdarzenia.Związane z:
źródło
Cóż, najpierw zamieniłbym prostą tablicę par klucz-wartość w tablicę hierarchiczną
To może przekonwertować płaską tablicę z parent_id i id na hierarchiczną:
Następnie po prostu utwórz funkcję renderującą:
źródło
Podczas gdy Alexander-Konstantinov może początkowo wydawać się nie tak łatwe do odczytania, jest zarówno genialne, jak i wykładniczo lepsze pod względem wydajności, to powinno zostać uznane za najlepszą odpowiedź.
Dzięki kolego, na twoją cześć wykonałem benchmark, aby porównać 2 rozwiązania przedstawione w tym poście.
Miałem płaskie drzewo @ 250k z 6 poziomami, które musiałem przekonwertować i szukałem lepszego sposobu, aby to zrobić i uniknąć rekurencyjnych iteracji.
Rekursja a odniesienie:
Wynik mówi sam za siebie:
źródło
Cóż, aby przeanalizować UL i LI, byłoby to coś takiego:
Ale chciałbym zobaczyć rozwiązanie, które nie wymaga tak częstego iterowania tablicy ...
źródło
Oto co wymyśliłem:
wyjścia:
źródło
Zagnieżdżona relacja rodzic-dziecko Tablica
Pobieranie całego rekordu z bazy danych i tworzenie tablicy zagnieżdżonej.
Drukuj dane kategorii i podkategorii w formacie JSON
źródło
$ aa = $ this-> parseTree ($ tree);
źródło
Stare pytanie, ale ja też musiałem to zrobić, a przykłady z rekurencją przyprawiały mnie o ból głowy. W mojej bazie danych mamy
locations
tabelę, która byłaloca_id
PK (Child) i odwoływała się do siebieloca_parent_id
(Parent).Celem jest przedstawienie tej struktury w HTML. Proste zapytanie couyrse może zwrócić dane w ustalonym porządku, ale nie znalazłem wystarczająco dobrze, aby wyświetlić takie dane w naturalny sposób. To, czego naprawdę chciałem, to obsługa chodzenia po drzewach Oracle
LEVEL
aby pomóc w wyświetlaniu.Zdecydowałem się wykorzystać ideę „ścieżki”, aby jednoznacznie zidentyfikować każdy wpis. Na przykład:
Posortowanie tablicy według ścieżki powinno ułatwić przetwarzanie w celu uzyskania sensownego wyświetlania.
Zdaję sobie sprawę, że użycie tablic asocjacyjnych i sortowań jest oszustwem, ponieważ ukrywa rekurencyjną złożoność operacji, ale dla mnie wygląda to prościej:
źródło
Jak stworzyć dynamiczny widok drzewa i menu
Krok 1: Najpierw utworzymy tabelę treeview w bazie danych mysql. ta tabela zawiera cztery kolumny. id to identyfikator zadania, a nazwa to nazwa zadania.
Krok 2: Metoda rekurencyjna widoku drzewa Stworzyłem poniżej metodę createTreeView () drzewa, która wywołuje rekursywną, jeśli identyfikator bieżącego zadania jest większy niż identyfikator poprzedniego zadania.
Krok 3: Utwórz plik indeksu, aby wyświetlić widok drzewa. To jest główny plik przykładu treeview, tutaj wywołamy metodę createTreeView () z wymaganymi parametrami.
Krok 4: Utwórz plik CSS style.css Tutaj napiszemy wszystkie klasy związane z CSS, obecnie używam listy zamówień do tworzenia widoku drzewa. możesz także zmienić ścieżkę obrazu tutaj.
Więcej szczegółów
źródło