Для этого требуется очень простая рекурсивная функция для синтаксического анализа пар дочерний / родительский для древовидной структуры и другая рекурсивная функция для ее вывода. Достаточно только одной функции, но вот две для ясности (комбинированную функцию можно найти в конце этого ответа).
Сначала инициализируйте массив пар дочерний / родительский:
$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null
);
Затем функция, которая анализирует этот массив в иерархическую древовидную структуру:
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;
}
И функция, которая обходит это дерево, чтобы распечатать неупорядоченный список:
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>';
}
}
И фактическое использование:
$result = parseTree($tree);
printTree($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
)
)
)
)
)
)
)
)
Если вы хотите немного большей эффективности, вы можете объединить эти функции в одну и уменьшить количество выполняемых итераций:
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>';
}
}
Вы сохраните только 8 итераций в таком маленьком наборе данных, но для больших наборов это может иметь значение.
Еще одна функция для создания дерева (без рекурсии, вместо этого используются ссылки):
Возвращает иерархический массив, подобный этому:
Что можно легко распечатать как список HTML с помощью рекурсивной функции.
источник
Другой, более упрощенный способ преобразования плоской структуры в
$tree
иерархию. Для его открытия нужен только один временный массив:Вот и все, чтобы превратить иерархию в многомерный массив:
Результат будет менее тривиальным, если вы хотите избежать рекурсии (это может стать бременем для больших структур).
Я всегда хотел решить «дилемму» UL / LI для вывода массива. Дилемма состоит в том, что каждый элемент не знает, будут ли дети следить за ним или сколько предшествующих элементов необходимо закрыть. В другом ответе я уже решил это, используя
RecursiveIteratorIterator
и ищаgetDepth()
и другую метаинформацию, которую я написалIterator
: Получение модели вложенного набора в<ul>
скрытые «закрытые» поддеревья . Этот ответ также показывает, что с итераторами вы достаточно гибки.Однако это был предварительно отсортированный список, поэтому он не подходит для вашего примера. Кроме того, я всегда хотел решить эту проблему для стандартной древовидной структуры, HTML-кодов
<ul>
и<li>
элементов.Основная идея, которую я придумал, заключается в следующем:
TreeNode
- Преобразует каждый элемент в простойTreeNode
тип, который может предоставить его значение (напримерName
), а также наличие у него дочерних элементов .TreeNodesIterator
- A,RecursiveIterator
который может перебирать набор (массив) из нихTreeNodes
. Это довольно просто, посколькуTreeNode
тип уже знает, есть ли у него потомки и какие.RecursiveListIterator
- ARecursiveIteratorIterator
, у которого есть все необходимые события, когда он рекурсивно перебирает любой типRecursiveIterator
:beginIteration
/endIteration
- Начало и конец основного списка.beginElement
/endElement
- Начало и конец каждого элемента.beginChildren
/endChildren
- Начало и конец каждого списка детей. ЭтоRecursiveListIterator
только предоставляет эти события в форме вызовов функций. дочерние списки, как это типично для<ul><li>
списков, открываются и закрываются внутри родительского<li>
элемента. Следовательно,endElement
событие запускается после соответствующегоendChildren
события. Это можно изменить или сделать настраиваемым, чтобы расширить использование этого класса. Затем события распределяются как вызовы функций для объекта-декоратора, чтобы отделить друг друга.ListDecorator
- Класс "декоратор", который является приемником событийRecursiveListIterator
.Начну с основной выходной логики. В виде иерархического
$tree
массива окончательный код выглядит следующим образом:Сначала давайте посмотрим на то,
ListDecorator
что просто обертывает<ul>
и<li>
элементы и решает о том , как структура списка выводится:Конструктор принимает итератор списка, над которым он работает.
inset
это просто вспомогательная функция для хорошего отступа вывода. Остальные - это просто функции вывода для каждого события:Имея в виду эти функции вывода, это снова основной цикл / завершение вывода, я прохожу его шаг за шагом:
Создайте корень,
TreeNode
который будет использоваться для запуска итерации:Это
TreeNodesIterator
является ,RecursiveIterator
что позволяет рекурсивной итерации над одним$root
узлом. Он передается как массив, потому что этому классу нужно что-то перебирать, и он позволяет повторно использовать набор дочерних элементов, который также является массивомTreeNode
элементов.Это
RecursiveListIterator
то,RecursiveIteratorIterator
что обеспечивает указанные события. Чтобы использовать его,ListDecorator
необходимо предоставить только a (класс выше) и присвоить емуaddDecorator
:Затем все настраивается так, чтобы просто
foreach
над ним и выводить каждый узел:Как показывает этот пример, вся логика вывода заключена в
ListDecorator
класс и этот единственныйforeach
. Весь рекурсивный обход был полностью инкапсулирован в рекурсивные итераторы SPL, которые обеспечивали составную процедуру, что означает, что внутренне вызовы функций рекурсии не выполняются.На основе событий
ListDecorator
позволяет вам специально модифицировать вывод и предоставлять несколько типов списков для одной и той же структуры данных. Можно даже изменить ввод, поскольку данные массива были инкапсулированыTreeNode
.Полный пример кода:
Outpupt:
Демо (вариант PHP 5.2)
Возможный вариант - итератор, который выполняет итерацию по любому
RecursiveIterator
итерацию по и обеспечивает итерацию по всем событиям, которые могут произойти. Затем переключатель / case внутри цикла foreach может обрабатывать события.Связанный:
источник
Ну, сначала я бы превратил прямой массив пар ключ-значение в иерархический массив
Это может преобразовать плоский массив с parent_id и id в иерархический:
Затем просто создайте функцию рендеринга:
источник
Хотя решение Александра-Константинова поначалу может показаться не таким легким для чтения, оно одновременно гениально и экспоненциально лучше с точки зрения производительности, но это должно было быть признано лучшим ответом.
Спасибо, дружище, я сделал тест в твою честь, чтобы сравнить 2 решения, представленные в этом посте.
У меня было плоское дерево @ 250k с 6 уровнями, которые мне пришлось преобразовать, и я искал лучший способ сделать это и избежать рекурсивных итераций.
Рекурсия против ссылки:
Результат говорит сам за себя:
источник
Что ж, если разобрать UL и LI, это будет примерно так:
Но я бы хотел увидеть решение, которое не требует, чтобы вы так часто перебирали массив ...
источник
Вот что я придумал:
выходы:
источник
Вложенный массив родительско-дочерних отношений
Извлечь все записи из базы данных и создать вложенный массив.
Печать данных категорий и подкатегорий в формате json
источник
$ aa = $ this-> parseTree ($ tree);
источник
Старый вопрос, но мне тоже пришлось это сделать, и примеры с рекурсией вызвали у меня головную боль. В моей базе данных есть
locations
таблица, которая былаloca_id
PK (Child) и ссылалась на себяloca_parent_id
(Parent).Цель состоит в том, чтобы представить эту структуру в HTML. Простой запрос может вернуть данные в фиксированном порядке, но я обнаружил, что недостаточно хорошо, чтобы отображать такие данные естественным образом. Что мне действительно нужно, так это обработка обхода дерева Oracle с помощью
LEVEL
чтобы помочь с отображением.Я решил использовать идею «пути», чтобы однозначно идентифицировать каждую запись. Например:
Сортировка массива по пути должна упростить обработку для значимого отображения.
Я понимаю, что использование ассоциативных массивов и сортировок является обманом, поскольку скрывает рекурсивную сложность операций, но для меня это выглядит проще:
источник
Как создать динамическое древовидное представление и меню
Шаг 1: Сначала мы создадим таблицу древовидного представления в базе данных mysql. эта таблица содержит четыре столбца. id - это идентификатор задачи, а имя - это имя задачи.
Шаг 2: рекурсивный метод древовидного представления Я создал ниже метод tree createTreeView (), который вызывает рекурсивный вызов, если текущий идентификатор задачи больше, чем идентификатор предыдущей задачи.
Шаг 3: Создайте индексный файл для отображения в виде дерева. Это основной файл примера treeview, здесь мы вызовем метод createTreeView () с необходимыми параметрами.
Шаг 4: Создайте файл CSS style.css Здесь мы напишем весь связанный с CSS класс, в настоящее время я использую список заказов для создания представления в виде дерева. вы также можете изменить путь к изображению здесь.
Подробнее
источник