Esto requiere una función recursiva muy básica para analizar los pares hijo / padre en una estructura de árbol y otra función recursiva para imprimirla. Solo una función sería suficiente, pero aquí hay dos para mayor claridad (se puede encontrar una función combinada al final de esta respuesta).
Primero inicialice la matriz de pares hijo / padre:
$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null
);
Luego, la función que analiza esa matriz en una estructura de árbol jerárquica:
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;
}
Y una función que atraviesa ese árbol para imprimir una lista desordenada:
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>';
}
}
Y el uso real:
$result = parseTree($tree);
printTree($result);
Aquí está el contenido de $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
)
)
)
)
)
)
)
)
Si desea un poco más de eficiencia, puede combinar esas funciones en una y reducir la cantidad de iteraciones realizadas:
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>';
}
}
Solo guardará 8 iteraciones en un conjunto de datos tan pequeño como este, pero en conjuntos más grandes podría marcar la diferencia.
Otra función más para hacer un árbol (sin recursividad involucrada, usa referencias en su lugar):
Devuelve una matriz jerárquica como esta:
Que se puede imprimir fácilmente como una lista HTML utilizando la función recursiva.
fuente
Otra forma más simplificada de convertir la estructura plana
$tree
en una jerarquía. Solo se necesita una matriz temporal para exponerlo:Eso es todo para colocar la jerarquía en una matriz multidimensional:
El resultado es menos trivial si desea evitar la recursividad (puede ser una carga con estructuras grandes).
Siempre quise resolver el "dilema" de UL / LI para generar una matriz. El dilema es que cada elemento no sabe si los niños seguirán o no o cuántos elementos precedentes deben cerrarse. En otra respuesta, ya resolví eso usando un
RecursiveIteratorIterator
y buscandogetDepth()
y otra metainformación queIterator
proporcionó mi propio escrito : Obtener un modelo de conjunto anidado en un<ul>
subárbol "cerrado" pero oculto . Esa respuesta también muestra que con los iteradores eres bastante flexible.Sin embargo, esa era una lista ordenada previamente, por lo que no sería adecuada para su ejemplo. Además, siempre quise resolver esto para una especie de estructura de árbol estándar y HTML
<ul>
y<li>
elementos.El concepto básico que se me ocurrió es el siguiente:
TreeNode
- Resumen cada elemento en unTreeNode
tipo simple que puede proporcionar su valor (por ejemploName
) y si tiene hijos o no.TreeNodesIterator
- ARecursiveIterator
que puede iterar sobre un conjunto (matriz) de estosTreeNodes
. Eso es bastante simple porque elTreeNode
tipo ya sabe si tiene hijos y cuáles.RecursiveListIterator
- ARecursiveIteratorIterator
que tiene todos los eventos necesarios cuando itera de forma recursiva sobre cualquier tipo deRecursiveIterator
:beginIteration
/endIteration
- Comienzo y final de la lista principal.beginElement
/endElement
- Comienzo y final de cada elemento.beginChildren
/endChildren
- Comienzo y final de cada lista de niños. EstoRecursiveListIterator
solo proporciona estos eventos en forma de llamadas a funciones. las listas secundarias, como es típico en las<ul><li>
listas, se abren y cierran dentro de su<li>
elemento padre . Por lo tanto, elendElement
evento se dispara después delendChildren
evento correspondiente. Esto podría cambiarse o configurarse para ampliar el uso de esta clase. Los eventos se distribuyen como llamadas de función a un objeto decorador para mantener las cosas separadas.ListDecorator
- Una clase "decoradora" que es solo un receptor de los eventos deRecursiveListIterator
.Empiezo con la lógica de salida principal. Tomada la
$tree
matriz ahora jerárquica , el código final se parece a lo siguiente:En primer lugar mirada nos dejó en el
ListDecorator
que simplemente se envuelve las<ul>
y los<li>
elementos y está decidiendo sobre cómo la estructura de la lista de salida es:El constructor toma el iterador de lista en el que está trabajando.
inset
es solo una función auxiliar para una buena sangría de la salida. El resto son solo las funciones de salida para cada evento:Con estas funciones de salida en mente, este es el ciclo / resumen de salida principal nuevamente, lo reviso paso a paso:
Cree la raíz
TreeNode
que se utilizará para iniciar la iteración en:Esta
TreeNodesIterator
es unaRecursiveIterator
que permite la iteración recursiva sobre un solo$root
nodo. Se pasa como una matriz porque esa clase necesita algo sobre lo que iterar y permite la reutilización con un conjunto de elementos secundarios que también es una matriz deTreeNode
elementos.Este
RecursiveListIterator
es unRecursiveIteratorIterator
que proporciona dichos eventos. Para hacer uso de él, solo esListDecorator
necesario proporcionar un (la clase anterior) y asignarleaddDecorator
:Luego, todo está configurado para pasarlo
foreach
y generar cada nodo:Como muestra este ejemplo, toda la lógica de salida está encapsulada en la
ListDecorator
clase y este singleforeach
. Todo el recorrido recursivo se ha encapsulado completamente en iteradores recursivos SPL que proporcionaron un procedimiento apilado, lo que significa que internamente no se realizan llamadas a funciones de recursividad.El basado en eventos le
ListDecorator
permite modificar la salida específicamente y proporcionar múltiples tipos de listas para la misma estructura de datos. Incluso es posible cambiar la entrada ya que se han encapsulado los datos de la matrizTreeNode
.El ejemplo de código completo:
Outpupt:
Demostración (variante PHP 5.2)
Una posible variante sería un iterador que itera sobre cualquiera
RecursiveIterator
y proporciona una iteración sobre todos los eventos que pueden ocurrir. Un interruptor / caso dentro del bucle foreach podría lidiar con los eventos.Relacionado:
fuente
Bueno, primero convertiría la matriz directa de pares clave-valor en una matriz jerárquica
Eso puede convertir una matriz plana con parent_id e id en una jerárquica:
Luego, simplemente cree una función de renderizado:
fuente
Si bien la solución de Alexander-Konstantinov puede no parecer tan fácil de leer al principio, es genial y exponencialmente mejor en términos de rendimiento, esta debería haber sido votada como la mejor respuesta.
Gracias amigo, hice un benchmark en tu honor para comparar las 2 soluciones presentadas en este post.
Tenía un árbol plano @ 250k con 6 niveles que tenía que convertir y estaba buscando una mejor manera de hacerlo y evitar iteraciones recursivas.
Recurrencia vs referencia:
La salida habla por sí sola:
fuente
Bueno, para analizar UL y LI, sería algo como:
Pero me encantaría ver una solución que no requiera que recorra la matriz con tanta frecuencia ...
fuente
Esto es lo que se me ocurrió:
salidas:
fuente
Relación padre-hijo Matriz anidada
Obtiene todo el registro de la base de datos y crea una matriz anidada.
Imprimir datos de categorías y subcategorías en formato json
fuente
$ aa = $ this-> parseTree ($ árbol);
fuente
Pregunta vieja, pero yo también tuve que hacer esto y los ejemplos con recursividad me dieron dolor de cabeza. En mi base de datos tenemos una
locations
tabla, que era unloca_id
PK (Child) y una autorreferencialoca_parent_id
(Parent).El objetivo es representar esta estructura en HTML. Por supuesto, la consulta simple puede devolver los datos en un orden fijo, pero no encontré lo suficientemente bien como para mostrar dichos datos de una manera natural. Lo que realmente quería era el manejo del recorrido del árbol de Oracle
LEVEL
para ayudar con la visualización.Decidí utilizar la idea de una "ruta" para identificar de forma única cada entrada. Por ejemplo:
Ordenar la matriz por la ruta debería facilitar el proceso para una visualización significativa.
Me doy cuenta de que el uso de matrices y clases asociativas es una trampa, ya que oculta la complejidad recursiva de las operaciones, pero para mí esto parece más simple:
fuente
Cómo crear un menú y una vista de árbol dinámicos
Paso 1: Primero crearemos una tabla de vista de árbol en la base de datos mysql. esta tabla contiene cuatro columnas. id es el id de la tarea y name es el nombre de la tarea.
Paso 2: Método recursivo de vista de árbol que he creado debajo del método createTreeView () del árbol que llama recursivo si la identificación de la tarea actual es mayor que la identificación de la tarea anterior.
Paso 3: Cree un archivo de índice para mostrar la vista de árbol. Este es el archivo principal del ejemplo de vista de árbol. Aquí llamaremos al método createTreeView () con los parámetros requeridos.
Paso 4: Cree el archivo CSS style.css Aquí escribiremos todas las clases relacionadas con CSS, actualmente estoy usando la lista de pedidos para crear una vista de árbol. también puede cambiar la ruta de la imagen aquí.
Más detalles
fuente