Come costruire un albero in modo efficiente da una struttura piatta?


153

Ho un sacco di oggetti in una struttura piatta. Questi oggetti hanno IDuna ParentIDproprietà e quindi possono essere disposti in alberi. Non sono in un ordine particolare. Ogni ParentIDproprietà non corrisponde necessariamente con un IDnella struttura. Pertanto, potrebbero essere diversi alberi che emergono da questi oggetti.

Come elaboreresti questi oggetti per creare gli alberi risultanti?

Non sono così lontano da una soluzione, ma sono sicuro che sia tutt'altro che ottimale ...

Devo creare questi alberi per inserire i dati in un database, nell'ordine corretto.

Non ci sono riferimenti circolari. Un nodo è un RootNode quando ParentID == null o quando ParentID non può essere trovato negli altri oggetti


Cosa intendi con "creare"? Rendering in un'interfaccia utente? Conservare in modo gerarchico in XML o in un database?
RedFilter

Come si definisce un nodo senza parent (ovvero un nodo root). ParentID è null? ParentID = 0? Presumo che non ci siano riferimenti circolari corretti?
Jason Punyon

5
Trovo questa domanda piuttosto interessante.
nes1983,

Risposte:


120

Memorizza gli ID degli oggetti in una tabella hash mappando l'oggetto specifico. Enumera attraverso tutti gli oggetti e trova il loro genitore se esiste e aggiorna il suo puntatore genitore di conseguenza.

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);
}

5
che lingua è quella? (Lo prendo C #)
Jason S

3
Questo algo è (in notazione informale) O (3N), dove come soluzione O (1N) è facilmente realizzabile istanziando nodi parziali per genitori non "attraversati" O mantenendo una tabella di ricerca secondaria per figli di non istanziati i genitori. Probabilmente non importa per la maggior parte degli usi del mondo reale, ma potrebbe essere significativo su grandi set di dati.
Andrew Hanlon,

15
@AndrewHanlon forse dovresti postare il sol per 0 (1N)
Ced

1
La risposta di Martin Schmidt di seguito è molto simile a come la implementerei. Come si può vedere, utilizza un singolo loop e il resto sono operazioni di hash.
Andrew Hanlon,

26
O (3N) è solo O (N);)
JakeWilson801,

34

Basato sulla risposta di Mehrdad Afshari e sul commento di Andrew Hanlon per uno speedup, ecco la mia opinione.

Differenza importante rispetto all'attività originale: un nodo radice ha 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;
}

1
Bello, questo è fondamentalmente l'approccio a cui alludevo. Vorrei tuttavia utilizzare solo un nodo pseudo root (con ID = 0 e null Parent) e rimuovere il requisito di autoreferenziazione.
Andrew Hanlon,

L'unica cosa che manca in questo esempio è l'assegnazione del campo Parent in ogni nodo figlio. Per fare ciò, dobbiamo solo impostare il campo Genitore dopo aver aggiunto i figli alla sua Raccolta Genitore. In questo modo: parentNode.Children.Add (ourNode); ourNode.Parent = parentNode;
plauriola,

@plauriola Vero, grazie, l'ho aggiunto. Un'alternativa sarebbe rimuovere semplicemente la proprietà Parent, non è necessario per l'algoritmo core.
Martin Schmidt,

4
Dato che non riuscivo a trovare un modulo npm che implementasse una soluzione O (n), ho creato quello seguente (testato sull'unità, copertura del codice al 100%, solo 0,5 kb di dimensioni e include le digitazioni. Forse aiuta qualcuno: npmjs.com/package / performant-array-to-tree
Philip Stanislaus,

32

Ecco un semplice algoritmo JavaScript per analizzare una tabella piatta in una struttura ad albero padre / figlio che viene eseguita in 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);

cercando di convertire questo approccio in C #.
Hakan,

realizzato che se l'id parte da qualcosa di grosso come 1001, allora otteniamo l'indice dall'eccezione legata ...
hakan,

2
Suggerimento: usare console.log(JSON.stringify(root, null, 2));per stampare piuttosto l'output.
aloisdg si trasferisce su codidact.com il

14

Soluzione Python

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

Per esempio:

# (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)

produce:

{
    "1": {
        "4": {
            "10": {}, 
            "11": {
                "16": {}, 
                "17": {
                    "24": {}, 
                    "25": {}
                }
            }
        }, 
        "5": {
            "8": {}, 
            "9": {
                "20": {}, 
                "12": {
                    "22": {}, 
                    "23": {
                        "2": {}, 
                        "27": {}, 
                        "26": {}
                    }
                }, 
                "21": {}, 
                "7": {}
            }
        }
    }
}

Ciao. Come posso aggiungere un altro attributo nell'output? vale a dire. name, parent_id
ragazzo semplice

di gran lunga il più elegante!
ccpizza,

@simpleguy: la comprensione dell'elenco può essere spiegata nel caso in cui sia necessario un maggiore controllo, ad esempio: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

8

Versione JS che restituisce una radice o una matrice di radici ciascuna delle quali avrà una proprietà della matrice Children contenente i figli correlati. Non dipende dall'input ordinato, abbastanza compatto e non utilizza la ricorsione. Godere!

// 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)

2
Questa domanda ha 7 anni e ha già una risposta votata e accettata. Se pensi di avere una soluzione migliore, sarebbe bello aggiungere qualche spiegazione al tuo codice.
Jordi Nebot,

Questo approccio funziona bene per questo tipo di dati non ordinati.
Cody C

4

Ho trovato una fantastica versione JavaScript qui: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Diciamo che hai un array come questo:

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 vuoi avere gli oggetti nidificati in questo modo:

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}
        ]
    }
];

Ecco una funzione ricorsiva che lo rende possibile.

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;
}

usuage:

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);

Per ogni parentId sta eseguendo il loop dell'intero modello - non è O (N ^ 2)?
Ed Randall,

4

Per chiunque sia interessato a una versione C # della soluzione di Eugene, nota che node_list è accessibile come una mappa, quindi usa un Dizionario.

Tieni presente che questa soluzione funziona solo se la tabella è ordinata per 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]);
}

Il nodo è definito come segue.

class Node
{
    public int id { get; set; }
    public int parent_id { get; set; }
    public List<Node> children = new List<Node>();
}

1
È troppo vecchio ma l'elemento Elenco 8 new Node { parent_id = 7, id = 9 },impedisce node_list.Add(item.id, item);di essere completato perché la chiave non può essere ripetuta; è un errore di battitura; quindi, anziché id = 9 , digita id = 8
Marcelo Scofano il

Fisso. Grazie @MarceloScofano!
Joel Malone,

3

Ho scritto una soluzione generica in C # liberamente basata sulla risposta di @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;
  }
}

Giù votante, per favore commenta. Sarò felice di sapere cosa ho fatto di sbagliato.
HuBeZa

2

Ecco la soluzione java della risposta di 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;
    }
}

Dovresti spiegare un po 'qual è la tua idea dietro al codice.
Ziad Akiki,

È solo la traduzione Java della risposta precedente
Vimal Bhatt,

1

Per quanto vaga la domanda mi sembri, probabilmente creerei una mappa dall'ID all'oggetto reale. In pseudo-java (non ho verificato se funziona / compila), potrebbe essere qualcosa del tipo:

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

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

E per cercare ogni genitore:

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

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

Riutilizzando getRealObject(ID)e realizzando una mappa dall'oggetto a una raccolta di oggetti (o relativi ID), si ottiene anche una mappa padre-> figlio.


1

Posso farlo in 4 righe di codice e O (n log n) time, supponendo che Dictionary sia qualcosa di simile a una 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 ora ho letto che alcuni parentID sono falsi, quindi dimentica quanto sopra e fai questo:

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.

1

La maggior parte delle risposte presuppone che tu stia cercando di farlo al di fuori del database. Se i tuoi alberi sono di natura relativamente statica e devi solo mappare gli alberi nel database, potresti prendere in considerazione l'utilizzo di rappresentazioni di set nidificati sul lato del database. Dai un'occhiata ai libri di Joe Celko (o qui per una panoramica di Celko).

Se comunque legato a Oracle dbs, dai un'occhiata a CONNECT BY per approcci SQL semplici.

Con entrambi gli approcci, è possibile saltare completamente la mappatura degli alberi prima di caricare i dati nel database. Ho pensato che avrei offerto questo in alternativa, potrebbe essere del tutto inappropriato per le tue esigenze specifiche. L'intera parte "ordine corretto" della domanda originale implica in qualche modo che hai bisogno che l'ordine sia "corretto" nel db per qualche motivo? Questo potrebbe spingermi anche a maneggiare gli alberi lì.


1

Non è esattamente lo stesso di quello che cercava il richiedente, ma ho avuto difficoltà a avvolgere la testa attorno alle risposte formulate in modo ambiguo fornite qui, e penso ancora che questa risposta rientri nel titolo.

La mia risposta è per mappare una struttura piatta su un albero direttamente sull'oggetto dove tutto ciò che hai è un ParentIDsu ogni oggetto. ParentIDè nullo 0se è una radice. Di fronte al richiedente, presumo tutto ParentIDil punto valido su qualcos'altro nell'elenco:

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;

1

ecco un'implementazione ruby:

Verrà catalogato in base al nome dell'attributo o al risultato di una chiamata al metodo.

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>]}}}

1

La risposta accettata mi sembra troppo complessa, quindi sto aggiungendo una versione Ruby e NodeJS

Supponiamo che l'elenco dei nodi piatti abbia la seguente struttura:

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

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

Le funzioni che trasformeranno la struttura dell'elenco piatto sopra in un albero appaiono nel modo seguente

per 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

per 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)

};

Credo che il controllo sia nullnecessarioundefined
Ullauri,

@Ullauri null == undefined => truein NodeJS
Hirurg103

1

un modo elegante per farlo è rappresentare gli elementi nell'elenco come stringa contenente un elenco di genitori separato da punti e infine un valore:

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

Quando monti un albero, finiresti con qualcosa del tipo:

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

Ho una libreria di configurazione che implementa questa configurazione di override (albero) dagli argomenti della riga di comando (elenco). L'algoritmo per aggiungere un singolo elemento all'elenco di un albero è qui .


0

Sei bloccato usando solo quegli attributi? In caso contrario, potrebbe essere utile creare una matrice di nodi figlio, in cui è possibile scorrere tutti questi oggetti una volta per creare tali attributi. Da lì, seleziona il nodo con figli ma senza genitori e costruisci iterativamente il tuo albero dall'alto verso il basso.


0

versione 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());
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.