Isso requer uma função recursiva muito básica para analisar os pares filho / pai em uma estrutura de árvore e outra função recursiva para imprimi-la. Apenas uma função seria suficiente, mas aqui estão duas para maior clareza (uma função combinada pode ser encontrada no final desta resposta).
Inicialize primeiro a matriz de pares filho / pai:
$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null
);
Em seguida, a função que analisa essa matriz em uma estrutura de árvore hierá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;
}
E uma função que atravessa essa árvore para imprimir uma lista não ordenada:
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>';
}
}
E o uso real:
$result = parseTree($tree);
printTree($result);
Aqui está o conteúdo 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
)
)
)
)
)
)
)
)
Se você quiser um pouco mais de eficiência, pode combinar essas funções em uma e reduzir o número de iterações feitas:
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>';
}
}
Você salvará apenas 8 iterações em um conjunto de dados tão pequeno quanto este, mas em conjuntos maiores isso pode fazer a diferença.
Outra função para fazer uma árvore (sem recursão envolvida, usa referências em vez disso):
Retorna uma matriz hierárquica como esta:
Que pode ser facilmente impressa como uma lista HTML usando a função recursiva.
fonte
Outra maneira mais simplificada de converter a estrutura plana do
$tree
em uma hierarquia. Apenas uma matriz temporária é necessária para expô-lo:Isso é tudo para obter a hierarquia em uma matriz multidimensional:
A saída é menos trivial se você quiser evitar a recursão (pode ser um fardo com estruturas grandes).
Sempre quis resolver o "dilema" UL / LI para gerar uma matriz. O dilema é que cada item não sabe se os filhos farão o acompanhamento ou não ou quantos elementos precedentes precisam ser encerrados. Em outra resposta, já resolvi isso usando um
RecursiveIteratorIterator
e procurandogetDepth()
e outras meta-informaçõesIterator
fornecidas por mim : Colocando o modelo de conjunto aninhado em um,<ul>
mas ocultando subárvores “fechadas” . Essa resposta mostra também que, com iteradores, você é bastante flexível.No entanto, essa era uma lista pré-classificada, portanto, não seria adequada para o seu exemplo. Além disso, sempre quis resolver isso para uma espécie de estrutura de árvore padrão e HTMLs
<ul>
e<li>
elementos.O conceito básico que criei é o seguinte:
TreeNode
- Abstrai cada elemento em umTreeNode
tipo simples que pode fornecer seu valor (por exemploName
) e se ele tem ou não filhos.TreeNodesIterator
- UmRecursiveIterator
que é capaz de iterar sobre um conjunto (array) dessesTreeNodes
. Isso é bastante simples, pois oTreeNode
tipo já sabe se tem filhos e quais.RecursiveListIterator
- ARecursiveIteratorIterator
que tem todos os eventos necessários quando itera recursivamente sobre qualquer tipo deRecursiveIterator
:beginIteration
/endIteration
- Início e fim da lista principal.beginElement
/endElement
- Início e fim de cada elemento.beginChildren
/endChildren
- Início e fim de cada lista de filhos. IssoRecursiveListIterator
só fornece esses eventos na forma de chamadas de função. listas filhas, como é típico para<ul><li>
listas, são abertas e fechadas dentro de seu<li>
elemento pai . Portanto, oendElement
evento é disparado após oendChildren
evento correspondente. Isso pode ser alterado ou configurado para ampliar o uso dessa classe. Os eventos são distribuídos como chamadas de função para um objeto decorador, para manter as coisas separadas.ListDecorator
- Uma classe "decorador" que é apenas um receptor dos eventos deRecursiveListIterator
.Eu começo com a lógica de saída principal. Considerando a
$tree
matriz agora hierárquica , o código final se parece com o seguinte:Olhar Primeiro vamos para o
ListDecorator
que simplesmente envolve os<ul>
e<li>
elementos e é decidir sobre a forma como a estrutura da lista é a saída:O construtor pega o iterador de lista no qual está trabalhando.
inset
é apenas uma função auxiliar para uma boa indentação da saída. O resto são apenas as funções de saída para cada evento:Com essas funções de saída em mente, este é o resumo / loop de saída principal novamente, passo a passo:
Crie a raiz
TreeNode
que será usada para iniciar a iteração em:Este
TreeNodesIterator
é umRecursiveIterator
que permite a iteração recursiva sobre o único$root
nó. É passado como um array porque essa classe precisa de algo para iterar e permite a reutilização com um conjunto de filhos que também é um array deTreeNode
elementos.Este
RecursiveListIterator
é umRecursiveIteratorIterator
que fornece os referidos eventos. Para fazer uso dele, apenas umListDecorator
precisa ser fornecido (a classe acima) e atribuído comaddDecorator
:Em seguida, tudo é configurado para apenas
foreach
sobre ele e a saída de cada nó:Como mostra este exemplo, toda a lógica de saída é encapsulada na
ListDecorator
classe e neste únicoforeach
. Todo o percurso recursivo foi totalmente encapsulado em iteradores recursivos SPL que forneceram um procedimento empilhado, o que significa que internamente nenhuma chamada de função de recursão é feita.O evento baseado em
ListDecorator
permite que você modifique a saída especificamente e forneça vários tipos de listas para a mesma estrutura de dados. É ainda possível alterar a entrada conforme os dados do array foram encapsulados emTreeNode
.O exemplo de código completo:
Outpupt:
Demonstração (variante do PHP 5.2)
Uma possível variante seria um iterador que itera sobre qualquer um
RecursiveIterator
e fornece uma iteração sobre todos os eventos que podem ocorrer. Um switch / case dentro do loop foreach poderia então lidar com os eventos.Relacionado:
fonte
Bem, primeiro eu transformaria a matriz direta de pares de valores-chave em uma matriz hierárquica
Isso pode converter uma matriz plana com parent_id e id em uma hierárquica:
Em seguida, basta criar uma função de renderização:
fonte
Enquanto Alexander-Konstantinov a solução de possa não parecer tão fácil de ler no início, ela é genial e exponencialmente melhor em termos de desempenho, ela deveria ter sido votada como a melhor resposta.
Obrigado colega, fiz um benchmark em sua homenagem para comparar as 2 soluções apresentadas neste post.
Eu tinha uma árvore plana do @ 250k com 6 níveis que precisava converter e estava procurando a melhor maneira de fazer isso e evitar iterações recursivas.
Recursão vs referência:
O resultado fala por si:
fonte
Bem, para analisar em ULs e LIs, seria algo como:
Mas adoraria ver uma solução que não exija que você repita o array com tanta frequência ...
fonte
Aqui está o que eu inventei:
saídas:
fonte
Matriz aninhada de relacionamento pai-filho
Busca todos os registros do banco de dados e cria uma matriz aninhada.
Imprimir dados de categorias e subcategorias em formato json
fonte
$ aa = $ this-> parseTree ($ tree);
fonte
Pergunta antiga, mas eu também tive que fazer isso e os exemplos com recursão me deram dor de cabeça. Em meu banco de dados, temos uma
locations
tabela, que era umloca_id
PK (Criança) e autorreferenciadaloca_parent_id
(Pai).O objetivo é representar essa estrutura em HTML. A consulta simples pode, claro, retornar os dados em alguma ordem fixa, mas não achei bem o suficiente para exibir esses dados de uma forma natural. O que eu realmente queria era o Oracle tree walk
LEVEL
para ajudar na exibição.Decidi usar a ideia de um 'caminho' para identificar de forma única cada entrada. Por exemplo:
Classificar a matriz pelo caminho deve facilitar o processamento para uma exibição significativa.
Percebo que o uso de matrizes e classificações associativas é enganoso, pois esconde a complexidade recursiva das operações, mas para mim isso parece mais simples:
fonte
Como criar menu e visualização em árvore dinâmica
Etapa 1: Primeiro, criaremos uma tabela treeview no banco de dados mysql. esta tabela contém quatro column.id é o id da tarefa e name é o nome da tarefa.
Etapa 2: Método recursivo de visualização em árvore que criei abaixo do método createTreeView () de árvore que chama recursivo se o id da tarefa atual for maior do que o id da tarefa anterior.
Etapa 3: Crie um arquivo de índice para mostrar a visualização em árvore. Este é o arquivo principal do exemplo de treeview. Aqui, chamaremos o método createTreeView () com os parâmetros necessários.
Etapa 4: Criar o arquivo CSS style.css Aqui, escreveremos todas as classes relacionadas ao CSS, atualmente estou usando a lista de pedidos para criar a visualização em árvore. você também pode alterar o caminho da imagem aqui.
Mais detalhes
fonte