Como construir eficientemente uma árvore a partir de uma estrutura plana?

153

Eu tenho um monte de objetos em uma estrutura plana. Esses objetos têm uma propriedade IDe uma ParentIDpropriedade para que possam ser organizados em árvores. Eles não estão em uma ordem específica. Cada ParentIDpropriedade não corresponde necessariamente a uma IDna estrutura. Portanto, podem haver várias árvores emergindo desses objetos.

Como você processaria esses objetos para criar as árvores resultantes?

Não estou tão longe de uma solução, mas tenho certeza de que está longe de ser a ideal ...

Preciso criar essas árvores para inserir dados em um banco de dados, na ordem correta.

Não há referências circulares. Um nó é um RootNode quando ParentID == null ou quando ParentID não pode ser encontrado nos outros objetos

Costo
fonte
O que você quer dizer com "criar"? Renderizar em uma interface do usuário? Armazenar de forma hierárquica em XML ou em um banco de dados?
RedFilter 14/01/09
Como você define um nó sem pai (ou seja, um nó raiz). ParentID é nulo? ParentID = 0? Presumo que não há referências circulares corretas?
Jason Punyon
5
Acho essa pergunta bem legal.
N191983
1
confira este artigo: scip.be/index.php?Page=ArticlesNET23&Lang=NL
ebram khalil

Respostas:

120

Armazene IDs dos objetos em um mapeamento de tabela de hash para o objeto específico. Enumere todos os objetos e encontre seu pai, se existir, e atualize seu ponteiro pai de acordo.

class MyObject
{ // The actual object
    public int ParentID { get; set; }
    public int ID { get; set; }
}

class Node
{
    public List<Node> Children = new List<Node>();
    public Node Parent { get; set; }
    public MyObject AssociatedObject { get; set; }
}

IEnumerable<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects)
{
    Dictionary<int, Node> lookup = new Dictionary<int, Node>();
    actualObjects.ForEach(x => lookup.Add(x.ID, new Node { AssociatedObject = x }));
    foreach (var item in lookup.Values) {
        Node proposedParent;
        if (lookup.TryGetValue(item.AssociatedObject.ParentID, out proposedParent)) {
            item.Parent = proposedParent;
            proposedParent.Children.Add(item);
        }
    }
    return lookup.Values.Where(x => x.Parent == null);
}
Mehrdad Afshari
fonte
5
que língua é essa? (Eu tomo C #) #
10241 Jason S
3
Esse algo é (em notação informal) O (3N), onde como uma solução O (1N) é facilmente alcançável instanciando Nós parciais para pais não 'atravessados' OU mantendo uma tabela de consulta secundária para filhos de filhos não instanciados pais. Provavelmente não importa para a maioria dos usos no mundo real, mas pode ser significativo em grandes conjuntos de dados.
Andrew Hanlon
15
@AndrewHanlon talvez você deva postar o sol para 0 (1N) #
181
1
A resposta de @Ced Martin Schmidt abaixo está muito próxima de como eu a implementaria. Como pode ser visto, ele usa um único loop e o restante são operações de hashtable.
Andrew Hanlon
26
O (3N) é apenas O (N);)
JakeWilson801
34

Com base na resposta de Mehrdad Afshari e no comentário de Andrew Hanlon para uma aceleração, aqui está minha opinião.

Diferença importante para a tarefa original: Um nó raiz possui ID == parentID.

class MyObject
{   // The actual object
    public int ParentID { get; set; }
    public int ID { get; set; }
}

class Node
{
    public List<Node> Children = new List<Node>();
    public Node Parent { get; set; }
    public MyObject Source { get; set; }
}

List<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects)
{
    var lookup = new Dictionary<int, Node>();
    var rootNodes = new List<Node>();

    foreach (var item in actualObjects)
    {
        // add us to lookup
        Node ourNode;
        if (lookup.TryGetValue(item.ID, out ourNode))
        {   // was already found as a parent - register the actual object
            ourNode.Source = item;
        }
        else
        {
            ourNode = new Node() { Source = item };
            lookup.Add(item.ID, ourNode);
        }

        // hook into parent
        if (item.ParentID == item.ID)
        {   // is a root node
            rootNodes.Add(ourNode);
        }
        else
        {   // is a child row - so we have a parent
            Node parentNode;
            if (!lookup.TryGetValue(item.ParentID, out parentNode))
            {   // unknown parent, construct preliminary parent
                parentNode = new Node();
                lookup.Add(item.ParentID, parentNode);
            }
            parentNode.Children.Add(ourNode);
            ourNode.Parent = parentNode;
        }
    }

    return rootNodes;
}
Martin Schmidt
fonte
1
Bom, essa é basicamente a abordagem que eu estava aludindo. No entanto, eu usaria apenas um nó pseudo-raiz (com ID = 0 e pai nulo) e removeria o requisito de auto-referência.
Andrew Hanlon
A única coisa que falta neste exemplo é atribuir o campo Pai a todos os nós filhos. Para fazer isso, só precisamos definir o campo Pai depois de adicionar os filhos à sua Coleção pai. Assim: parentNode.Children.Add (ourNode); ourNode.Parent = parentNode;
31516 plauriola
@plauriola É verdade, obrigado, eu adicionei isso. Uma alternativa seria apenas remover a propriedade Parent, não é necessário para o algoritmo principal.
Martin Schmidt
4
Como não consegui encontrar um módulo npm que implemente uma solução O (n), criei o seguinte (testado por unidade, 100% de cobertura de código, apenas 0,5 kb de tamanho e incluindo tipografia. Talvez ajude alguém: npmjs.com/package / performant-array-to-tree
Philip Stanislaus
32

Aqui está um algoritmo JavaScript simples para analisar uma tabela plana em uma estrutura de árvore pai / filho que é executada em N tempo:

var table = [
    {parent_id: 0, id: 1, children: []},
    {parent_id: 0, id: 2, children: []},
    {parent_id: 0, id: 3, children: []},
    {parent_id: 1, id: 4, children: []},
    {parent_id: 1, id: 5, children: []},
    {parent_id: 1, id: 6, children: []},
    {parent_id: 2, id: 7, children: []},
    {parent_id: 7, id: 8, children: []},
    {parent_id: 8, id: 9, children: []},
    {parent_id: 3, id: 10, children: []}
];

var root = {id:0, parent_id: null, children: []};
var node_list = { 0 : root};

for (var i = 0; i < table.length; i++) {
    node_list[table[i].id] = table[i];
    node_list[table[i].parent_id].children.push(node_list[table[i].id]);
}

console.log(root);
Eugene
fonte
tentando converter essa abordagem em c #.
21717 hakan
percebeu que se ID começar de grande algo como 1001, então temos índice fora de exceção limite ...
hakan
2
Dica: use console.log(JSON.stringify(root, null, 2));para imprimir bastante a saída.
aloisdg movendo-se para codidact.com 17/10
14

Solução Python

def subtree(node, relationships):
    return {
        v: subtree(v, relationships) 
        for v in [x[0] for x in relationships if x[1] == node]
    }

Por exemplo:

# (child, parent) pairs where -1 means no parent    
flat_tree = [
     (1, -1),
     (4, 1),
     (10, 4),
     (11, 4),
     (16, 11),
     (17, 11),
     (24, 17),
     (25, 17),
     (5, 1),
     (8, 5),
     (9, 5),
     (7, 9),
     (12, 9),
     (22, 12),
     (23, 12),
     (2, 23),
     (26, 23),
     (27, 23),
     (20, 9),
     (21, 9)
    ]

subtree(-1, flat_tree)

Produz:

{
    "1": {
        "4": {
            "10": {}, 
            "11": {
                "16": {}, 
                "17": {
                    "24": {}, 
                    "25": {}
                }
            }
        }, 
        "5": {
            "8": {}, 
            "9": {
                "20": {}, 
                "12": {
                    "22": {}, 
                    "23": {
                        "2": {}, 
                        "27": {}, 
                        "26": {}
                    }
                }, 
                "21": {}, 
                "7": {}
            }
        }
    }
}
minkwe
fonte
Oi. Como adiciono outro atributo na saída? ie nome, parent_id
simple guy
de longe o mais elegante!
ccpizza 28/04
@simpleguy: a compreensão da lista pode ser revelada caso você precise de mais controle, por exemplo:def recurse(id, pages): for row in rows: if row['id'] == id: print(f'''{row['id']}:{row['parent_id']} {row['path']} {row['title']}''') recurse(row['id'], rows)
ccpizza 29/04
8

Versão JS que retorna uma raiz ou uma matriz de raízes, cada uma das quais terá uma propriedade da matriz Filhos contendo os filhos relacionados. Não depende da entrada ordenada, compacta decentemente e não usa recursão. Aproveitar!

// creates a tree from a flat set of hierarchically related data
var MiracleGrow = function(treeData, key, parentKey)
{
    var keys = [];
    treeData.map(function(x){
        x.Children = [];
        keys.push(x[key]);
    });
    var roots = treeData.filter(function(x){return keys.indexOf(x[parentKey])==-1});
    var nodes = [];
    roots.map(function(x){nodes.push(x)});
    while(nodes.length > 0)
    {

        var node = nodes.pop();
        var children =  treeData.filter(function(x){return x[parentKey] == node[key]});
        children.map(function(x){
            node.Children.push(x);
            nodes.push(x)
        });
    }
    if (roots.length==1) return roots[0];
    return roots;
}


// demo/test data
var treeData = [

    {id:9, name:'Led Zep', parent:null},
    {id:10, name:'Jimmy', parent:9},
    {id:11, name:'Robert', parent:9},
    {id:12, name:'John', parent:9},

    {id:8, name:'Elec Gtr Strings', parent:5},
    {id:1, name:'Rush', parent:null},
    {id:2, name:'Alex', parent:1},
    {id:3, name:'Geddy', parent:1},
    {id:4, name:'Neil', parent:1},
    {id:5, name:'Gibson Les Paul', parent:2},
    {id:6, name:'Pearl Kit', parent:4},
    {id:7, name:'Rickenbacker', parent:3},

    {id:100, name:'Santa', parent:99},
    {id:101, name:'Elf', parent:100},

];
var root = MiracleGrow(treeData, "id", "parent")
console.log(root)
nu11ptr
fonte
2
Esta pergunta tem 7 anos e já tem uma resposta votada e aceita. Se você acha que tem uma solução melhor, seria ótimo adicionar algumas explicações ao seu código.
perfil completo de Jordi Nebot
Essa abordagem funciona bem para esse tipo não ordenado de dados.
Cody C
4

Encontrei uma versão JavaScript incrível aqui: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Digamos que você tenha uma matriz como esta:

const models = [
    {id: 1, title: 'hello', parent: 0},
    {id: 2, title: 'hello', parent: 0},
    {id: 3, title: 'hello', parent: 1},
    {id: 4, title: 'hello', parent: 3},
    {id: 5, title: 'hello', parent: 4},
    {id: 6, title: 'hello', parent: 4},
    {id: 7, title: 'hello', parent: 3},
    {id: 8, title: 'hello', parent: 2}
];

E você deseja que os objetos sejam aninhados assim:

const nestedStructure = [
    {
        id: 1, title: 'hello', parent: 0, children: [
            {
                id: 3, title: 'hello', parent: 1, children: [
                    {
                        id: 4, title: 'hello', parent: 3, children: [
                            {id: 5, title: 'hello', parent: 4},
                            {id: 6, title: 'hello', parent: 4}
                        ]
                    },
                    {id: 7, title: 'hello', parent: 3}
                ]
            }
        ]
    },
    {
        id: 2, title: 'hello', parent: 0, children: [
            {id: 8, title: 'hello', parent: 2}
        ]
    }
];

Aqui está uma função recursiva que faz isso acontecer.

function getNestedChildren(models, parentId) {
    const nestedTreeStructure = [];
    const length = models.length;

    for (let i = 0; i < length; i++) { // for-loop for perf reasons, huge difference in ie11
        const model = models[i];

        if (model.parent == parentId) {
            const children = getNestedChildren(models, model.id);

            if (children.length > 0) {
                model.children = children;
            }

            nestedTreeStructure.push(model);
        }
    }

    return nestedTreeStructure;
}

Uso:

const models = [
    {id: 1, title: 'hello', parent: 0},
    {id: 2, title: 'hello', parent: 0},
    {id: 3, title: 'hello', parent: 1},
    {id: 4, title: 'hello', parent: 3},
    {id: 5, title: 'hello', parent: 4},
    {id: 6, title: 'hello', parent: 4},
    {id: 7, title: 'hello', parent: 3},
    {id: 8, title: 'hello', parent: 2}
];
const nestedStructure = getNestedChildren(models, 0);
codeBelt
fonte
Para cada parentId, ele faz um loop em todo o modelo - não é O (N ^ 2)?
Ed Randall
4

Para qualquer pessoa interessada em uma versão C # da solução de Eugene, observe que o node_list é acessado como um mapa; portanto, use um dicionário.

Lembre-se de que esta solução só funciona se a tabela for classificada por parent_id .

var table = new[]
{
    new Node { parent_id = 0, id = 1 },
    new Node { parent_id = 0, id = 2 },
    new Node { parent_id = 0, id = 3 },
    new Node { parent_id = 1, id = 4 },
    new Node { parent_id = 1, id = 5 },
    new Node { parent_id = 1, id = 6 },
    new Node { parent_id = 2, id = 7 },
    new Node { parent_id = 7, id = 8 },
    new Node { parent_id = 8, id = 9 },
    new Node { parent_id = 3, id = 10 },
};

var root = new Node { id = 0 };
var node_list = new Dictionary<int, Node>{
    { 0, root }
};

foreach (var item in table)
{
    node_list.Add(item.id, item);
    node_list[item.parent_id].children.Add(node_list[item.id]);
}

O nó é definido da seguinte maneira.

class Node
{
    public int id { get; set; }
    public int parent_id { get; set; }
    public List<Node> children = new List<Node>();
}
Joel Malone
fonte
1
É muito antigo, mas o Item da Lista 8 new Node { parent_id = 7, id = 9 },impede node_list.Add(item.id, item);a conclusão porque a Chave não pode se repetir; é um erro de digitação; então, em vez de id = 9 , digite id = 8
Marcelo Scofano
Fixo. Obrigado @MarceloScofano!
Joel Malone
3

Eu escrevi uma solução genérica em C # com base na resposta do @Mehrdad Afshari:

void Example(List<MyObject> actualObjects)
{
  List<TreeNode<MyObject>> treeRoots = actualObjects.BuildTree(obj => obj.ID, obj => obj.ParentID, -1);
}

public class TreeNode<T>
{
  public TreeNode(T value)
  {
    Value = value;
    Children = new List<TreeNode<T>>();
  }

  public T Value { get; private set; }
  public List<TreeNode<T>> Children { get; private set; }
}

public static class TreeExtensions
{
  public static List<TreeNode<TValue>> BuildTree<TKey, TValue>(this IEnumerable<TValue> objects, Func<TValue, TKey> keySelector, Func<TValue, TKey> parentKeySelector, TKey defaultKey = default(TKey))
  {
    var roots = new List<TreeNode<TValue>>();
    var allNodes = objects.Select(overrideValue => new TreeNode<TValue>(overrideValue)).ToArray();
    var nodesByRowId = allNodes.ToDictionary(node => keySelector(node.Value));

    foreach (var currentNode in allNodes)
    {
      TKey parentKey = parentKeySelector(currentNode.Value);
      if (Equals(parentKey, defaultKey))
      {
        roots.Add(currentNode);
      }
      else
      {
        nodesByRowId[parentKey].Children.Add(currentNode);
      }
    }

    return roots;
  }
}
HuBeZa
fonte
Abaixo eleitor, comente. Ficarei feliz em saber o que fiz de errado.
precisa saber é o seguinte
2

Aqui está a solução java da resposta de Mehrdad Afshari.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class Tree {

    Iterator<Node> buildTreeAndGetRoots(List<MyObject> actualObjects) {
        Map<Integer, Node> lookup = new HashMap<>();
        actualObjects.forEach(x -> lookup.put(x.id, new Node(x)));
        //foreach (var item in lookup.Values)
        lookup.values().forEach(item ->
                {
                    Node proposedParent;
                    if (lookup.containsKey(item.associatedObject.parentId)) {
                        proposedParent = lookup.get(item.associatedObject.parentId);
                        item.parent = proposedParent;
                        proposedParent.children.add(item);
                    }
                }
        );
        //return lookup.values.Where(x =>x.Parent ==null);
        return lookup.values().stream().filter(x -> x.parent == null).iterator();
    }

}

class MyObject { // The actual object
    public int parentId;
    public int id;
}

class Node {
    public List<Node> children = new ArrayList<Node>();
    public Node parent;
    public MyObject associatedObject;

    public Node(MyObject associatedObject) {
        this.associatedObject = associatedObject;
    }
}
Vimal Bhatt
fonte
Você deve explicar um pouco qual é a sua ideia por trás do código.
Ziad Akiki
É apenas uma tradução para Java da resposta anterior
Vimal Bhatt
1

Por mais vaga que a pergunta me pareça, provavelmente criaria um mapa a partir do ID para o objeto real. No pseudo-java (não verifiquei se funciona / compila), pode ser algo como:

Map<ID, FlatObject> flatObjectMap = new HashMap<ID, FlatObject>();

for (FlatObject object: flatStructure) {
    flatObjectMap.put(object.ID, object);
}

E para procurar cada pai:

private FlatObject getParent(FlatObject object) {
    getRealObject(object.ParentID);
}

private FlatObject getRealObject(ID objectID) {
    flatObjectMap.get(objectID);
}

Ao reutilizar getRealObject(ID)e fazer um mapa de objeto para uma coleção de objetos (ou seus IDs), você também obtém um mapa pai-> filho.

Henrik Paul
fonte
1

Eu posso fazer isso em 4 linhas de código e tempo O (n log n), assumindo que Dictionary seja algo como um TreeMap.

dict := Dictionary new.
ary do: [:each | dict at: each id put: each].
ary do: [:each | (dict at: each parent) addChild: each].
root := dict at: nil.

EDIT : Ok, e agora eu li que alguns parentIDs são falsos, então esqueça o acima e faça o seguinte:

dict := Dictionary new.
dict at: nil put: OrderedCollection new.
ary do: [:each | dict at: each id put: each].
ary do: [:each | 
    (dict at: each parent ifAbsent: [dict at: nil]) 
          add: each].
roots := dict at: nil.
nes1983
fonte
1

A maioria das respostas pressupõe que você esteja procurando fazer isso fora do banco de dados. Se suas árvores são relativamente estáticas por natureza e você só precisa mapear as árvores no banco de dados, considere o uso de representações de conjuntos aninhados no lado do banco de dados. Confira os livros de Joe Celko (ou aqui para uma visão geral de Celko). Com qualquer uma das abordagens, você pode pular completamente o mapeamento das árvores antes de carregar os dados no banco de dados. Apenas pensei em oferecer isso como uma alternativa, pode ser completamente inapropriado para suas necessidades específicas. Toda a parte "ordem correta" da pergunta original implica que você precisa que a ordem esteja "correta" no banco de dados por algum motivo? Isso pode me levar a lidar com as árvores lá também.

Se estiver vinculado aos dbs do Oracle de qualquer maneira, confira o CONNECT BY para obter abordagens SQL diretas.


fonte
1

Não é exatamente o mesmo que o solicitante procurou, mas tive dificuldade em entender as respostas formuladas de forma ambígua fornecidas aqui e ainda acho que essa resposta se encaixa no título.

Minha resposta é mapear uma estrutura plana para uma árvore diretamente no objeto, onde tudo o que você tem é um ParentIDem cada objeto. ParentIDé nullou 0se é uma raiz. Oposto ao solicitante, presumo que todos os pontos válidos sejam ParentIDpara outra coisa na lista:

var rootNodes = new List<DTIntranetMenuItem>();
var dictIntranetMenuItems = new Dictionary<long, DTIntranetMenuItem>();

//Convert the flat database items to the DTO's,
//that has a list of children instead of a ParentID.
foreach (var efIntranetMenuItem in flatIntranetMenuItems) //List<tblIntranetMenuItem>
{
    //Automapper (nuget)
    DTIntranetMenuItem intranetMenuItem =
                                   Mapper.Map<DTIntranetMenuItem>(efIntranetMenuItem);
    intranetMenuItem.Children = new List<DTIntranetMenuItem>();
    dictIntranetMenuItems.Add(efIntranetMenuItem.ID, intranetMenuItem);
}

foreach (var efIntranetMenuItem in flatIntranetMenuItems)
{
    //Getting the equivalent object of the converted ones
    DTIntranetMenuItem intranetMenuItem = dictIntranetMenuItems[efIntranetMenuItem.ID];

    if (efIntranetMenuItem.ParentID == null || efIntranetMenuItem.ParentID <= 0)
    {
        rootNodes.Add(intranetMenuItem);
    }
    else
    {
        var parent = dictIntranetMenuItems[efIntranetMenuItem.ParentID.Value];
        parent.Children.Add(intranetMenuItem);
        //intranetMenuItem.Parent = parent;
    }
}
return rootNodes;
Aske B.
fonte
1

aqui está uma implementação em ruby:

Ele catalogará pelo nome do atributo ou pelo resultado de uma chamada de método.

CatalogGenerator = ->(depth) do
  if depth != 0
    ->(hash, key) do
      hash[key] = Hash.new(&CatalogGenerator[depth - 1])
    end
  else
    ->(hash, key) do
      hash[key] = []
    end
  end
end

def catalog(collection, root_name: :root, by:)
  method_names = [*by]
  log = Hash.new(&CatalogGenerator[method_names.length])
  tree = collection.each_with_object(log) do |item, catalog|
    path = method_names.map { |method_name| item.public_send(method_name)}.unshift(root_name.to_sym)
  catalog.dig(*path) << item
  end
  tree.with_indifferent_access
end

 students = [#<Student:0x007f891d0b4818 id: 33999, status: "on_hold", tenant_id: 95>,
 #<Student:0x007f891d0b4570 id: 7635, status: "on_hold", tenant_id: 6>,
 #<Student:0x007f891d0b42c8 id: 37220, status: "on_hold", tenant_id: 6>,
 #<Student:0x007f891d0b4020 id: 3444, status: "ready_for_match", tenant_id: 15>,
 #<Student:0x007f8931d5ab58 id: 25166, status: "in_partnership", tenant_id: 10>]

catalog students, by: [:tenant_id, :status]

# this would out put the following
{"root"=>
  {95=>
    {"on_hold"=>
      [#<Student:0x007f891d0b4818
        id: 33999,
        status: "on_hold",
        tenant_id: 95>]},
   6=>
    {"on_hold"=>
      [#<Student:0x007f891d0b4570 id: 7635, status: "on_hold", tenant_id: 6>,
       #<Student:0x007f891d0b42c8
        id: 37220,
        status: "on_hold",
        tenant_id: 6>]},
   15=>
    {"ready_for_match"=>
      [#<Student:0x007f891d0b4020
        id: 3444,
        status: "ready_for_match",
        tenant_id: 15>]},
   10=>
    {"in_partnership"=>
      [#<Student:0x007f8931d5ab58
        id: 25166,
        status: "in_partnership",
        tenant_id: 10>]}}}
joegiralt
fonte
1

A resposta aceita parece muito complexa para mim, por isso estou adicionando uma versão Ruby e NodeJS dela

Suponha que a lista de nós simples tenha a seguinte estrutura:

nodes = [
  { id: 7, parent_id: 1 },
  ...
] # ruby

nodes = [
  { id: 7, parentId: 1 },
  ...
] # nodeJS

As funções que transformarão a estrutura da lista plana acima em uma árvore têm a seguinte aparência

para Ruby:

def to_tree(nodes)

  nodes.each do |node|

    parent = nodes.find { |another| another[:id] == node[:parent_id] }
    next unless parent

    node[:parent] = parent
    parent[:children] ||= []
    parent[:children] << node

  end

  nodes.select { |node| node[:parent].nil? }

end

para NodeJS:

const toTree = (nodes) => {

  nodes.forEach((node) => {

    const parent = nodes.find((another) => another.id == node.parentId)
    if(parent == null) return;

    node.parent = parent;
    parent.children = parent.children || [];
    parent.children = parent.children.concat(node);

  });

  return nodes.filter((node) => node.parent == null)

};
Hirurg103
fonte
Eu acredito que o cheque nullprecisa ser paraundefined
Ullauri
@Ullauri null == undefined => truein NodeJS
Hirurg103
1

Uma maneira elegante de fazer isso é representar os itens da lista como uma string contendo uma lista de pais separada por pontos e, finalmente, um valor:

server.port=90
server.hostname=localhost
client.serverport=90
client.database.port=1234
client.database.host=localhost

Ao montar uma árvore, você acabaria com algo como:

server:
  port: 90
  hostname: localhost
client:
  serverport=1234
  database:
    port: 1234
    host: localhost

Eu tenho uma biblioteca de configuração que implementa essa configuração de substituição (árvore) nos argumentos da linha de comando (lista). O algoritmo para adicionar um único item à lista em uma árvore está aqui .

Omry Yadan
fonte
0

Você está preso usando apenas esses atributos? Caso contrário, pode ser bom criar uma matriz de nós filhos, onde você pode percorrer todos esses objetos uma vez para criar esses atributos. A partir daí, selecione o nó com filhos, mas sem pais, e construa iterativamente sua árvore de cima para baixo.

Robert Elwell
fonte
0

versão java

// node
@Data
public class Node {
    private Long id;
    private Long parentId;
    private String name;
    private List<Node> children = new ArrayList<>();
}

// flat list to tree
List<Node> nodes = new ArrayList();// load nodes from db or network
Map<Long, Node> nodeMap = new HashMap();
nodes.forEach(node -> {
  if (!nodeMap.containsKey(node.getId)) nodeMap.put(node.getId, node);
  if (nodeMap.containsKey(node.getParentId)) {
    Node parent = nodeMap.get(node.getParentId);
    node.setParentId(parent.getId());
    parent.getChildren().add(node);
  }
});

// tree node
List<Node> treeNode = nodeMap .values().stream().filter(n -> n.getParentId() == null).collect(Collectors.toList());
Aldom Michael
fonte