Comment construire efficacement un arbre à partir d'une structure plate?

153

J'ai un tas d'objets dans une structure plate. Ces objets ont une propriété IDet une ParentIDpropriété afin de pouvoir être disposés en arbres. Ils ne sont pas dans un ordre particulier. Chaque ParentIDpropriété ne correspond pas nécessairement à un IDdans la structure. Il pourrait donc y avoir plusieurs arbres émergeant de ces objets.

Comment traiteriez-vous ces objets pour créer les arbres résultants?

Je ne suis pas si loin d'une solution mais je suis sûr que c'est loin d'être optimal ...

Je dois créer ces arbres pour ensuite insérer des données dans une base de données, dans le bon ordre.

Il n'y a pas de références circulaires. Un nœud est un nœud racine lorsque ParentID == null ou lorsque ParentID est introuvable dans les autres objets

Costo
la source
Qu'entendez-vous par «créer»? Rendu dans une interface utilisateur? Stocker de manière hiérarchique en XML ou dans une base de données?
RedFilter le
Comment définir un nœud sans parent (c'est-à-dire un nœud racine). ParentID est nul? ParentID = 0? Je suppose qu'il n'y a pas de références circulaires correctes?
Jason Punyon
5
Je trouve cette question assez cool.
nes1983
1
Consultez cet article: scip.be/index.php?Page=ArticlesNET23&Lang=NL
ebram khalil

Réponses:

120

Stockez les ID des objets dans une table de hachage mappant à l'objet spécifique. Énumérer tous les objets et trouver leur parent s'il existe et mettre à jour son pointeur parent en conséquence.

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
la source
5
quelle langue est-ce? (Je le prends C #)
Jason S
3
Cet algorithme est (en notation informelle) O (3N), où une solution O (1N) est facilement réalisable en instanciant des nœuds partiels pour les parents non `` traversés '' OU en conservant une table de recherche secondaire pour les enfants de non-instanciés Parents. Cela n'a probablement pas d'importance pour la plupart des utilisations du monde réel, mais cela pourrait être important sur de grands ensembles de données.
Andrew Hanlon
15
@AndrewHanlon peut-être que vous devriez publier le sol pour 0 (1N)
Ced
1
La réponse de @Ced Martin Schmidt ci-dessous est très proche de la façon dont je la mettrais en œuvre. Comme on peut le voir, il utilise une seule boucle et le reste sont des opérations de hachage.
Andrew Hanlon
26
O (3N) est juste O (N);)
JakeWilson801
34

Sur la base de la réponse de Mehrdad Afshari et du commentaire d'Andrew Hanlon pour une accélération, voici mon avis.

Différence importante par rapport à la tâche d'origine: un nœud racine a l'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
la source
1
Bien, c'est essentiellement l'approche à laquelle je faisais allusion. Cependant, j'utiliserais simplement un pseudo nœud racine (avec ID = 0 et Parent nul) et supprimerais l'exigence d'auto-référence.
Andrew Hanlon
La seule chose qui manque dans cet exemple est l'affectation du champ Parent à chaque nœud enfant. Pour ce faire, il suffit de définir le champ Parent après avoir ajouté les enfants à sa collection Parent. Comme ceci: parentNode.Children.Add (ourNode); ourNode.Parent = parentNode;
plauriola
@plauriola C'est vrai, merci, j'ai ajouté ceci. Une alternative serait de simplement supprimer la propriété Parent, ce n'est pas nécessaire pour l'algorithme de base.
Martin Schmidt
4
Comme je n'ai pas trouvé de module npm qui implémente une solution O (n), j'ai créé le suivant (testé à l'unité, couverture de code à 100%, seulement 0,5 ko de taille et comprend des typages. Peut-être que cela aide quelqu'un: npmjs.com/package / performant-array-to-tree
Philip Stanislaus
32

Voici un algorithme JavaScript simple pour analyser une table plate en une arborescence parent / enfant qui s'exécute en N temps:

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);
Eugène
la source
essayer de convertir cette approche en C #.
hakan
réalisé que si id commence à partir de quelque chose de gros comme 1001, alors nous obtenons l'index hors de l'exception liée ...
hakan
2
Astuce: utilisez console.log(JSON.stringify(root, null, 2));pour imprimer la sortie.
aloisdg passe à codidact.com
14

Solution Python

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

Par exemple:

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

Produit:

{
    "1": {
        "4": {
            "10": {}, 
            "11": {
                "16": {}, 
                "17": {
                    "24": {}, 
                    "25": {}
                }
            }
        }, 
        "5": {
            "8": {}, 
            "9": {
                "20": {}, 
                "12": {
                    "22": {}, 
                    "23": {
                        "2": {}, 
                        "27": {}, 
                        "26": {}
                    }
                }, 
                "21": {}, 
                "7": {}
            }
        }
    }
}
minkwe
la source
Salut. Comment ajouter un autre attribut dans la sortie? c'est à dire. name, parent_id
simple guy
de loin le plus élégant!
ccpizza
@simpleguy: la compréhension de la liste peut être dépliée au cas où vous auriez besoin de plus de contrôle, par exemple: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

Version JS qui renvoie une racine ou un tableau de racines dont chacune aura une propriété de tableau Children contenant les enfants associés. Ne dépend pas d'une entrée ordonnée, compacte décemment et n'utilise pas de récursivité. Prendre plaisir!

// 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
la source
2
Cette question a 7 ans et a déjà une réponse votée et acceptée. Si vous pensez avoir une meilleure solution, ce serait bien d'ajouter quelques explications à votre code.
Jordi Nebot
Cette approche fonctionne bien pour ce type de données non ordonné.
Cody C
4

J'ai trouvé une version JavaScript géniale ici: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Disons que vous avez un tableau comme celui-ci:

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

Et vous voulez que les objets soient imbriqués comme ceci:

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

Voici une fonction récursive qui le rend possible.

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);
codeBelt
la source
Pour chaque parentId, il boucle tout le modèle - n'est-ce pas O (N ^ 2)?
Ed Randall
4

Pour toute personne intéressée par une version C # de la solution d'Eugene, notez que node_list est accessible sous forme de carte, utilisez donc un dictionnaire à la place.

Gardez à l'esprit que cette solution ne fonctionne que si la table est triée par 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]);
}

Le nœud est défini comme suit.

class Node
{
    public int id { get; set; }
    public int parent_id { get; set; }
    public List<Node> children = new List<Node>();
}
Joël Malone
la source
1
Il est trop ancien mais l'élément de liste 8 new Node { parent_id = 7, id = 9 },empêche node_list.Add(item.id, item);de se terminer car Key ne peut pas se répéter; c'est une faute de frappe; donc, au lieu de id = 9 , tapez id = 8
Marcelo Scofano
Fixé. Merci @MarceloScofano!
Joel Malone le
3

J'ai écrit une solution générique en C # vaguement basée sur la réponse @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
la source
Électeur bas, veuillez commenter. Je serai heureux de savoir ce que j'ai fait de mal.
HuBeZa
2

Voici la solution java de la réponse 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
la source
Vous devriez expliquer un peu quelle est votre idée derrière le code.
Ziad Akiki le
C'est juste la traduction Java de la réponse précédente
Vimal Bhatt
1

Aussi vague que la question me semble, je créerais probablement une carte de l'ID à l'objet réel. En pseudo-java (je n'ai pas vérifié si cela fonctionne / compile), cela pourrait être quelque chose comme:

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

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

Et pour rechercher chaque parent:

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

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

En réutilisant getRealObject(ID)et en effectuant une mappe d'un objet à une collection d'objets (ou leurs ID), vous obtenez également une mappe parent-> enfants.

Henrik Paul
la source
1

Je peux le faire en 4 lignes de code et en temps O (n log n), en supposant que Dictionary est quelque chose comme un 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, et maintenant j'ai lu que certains parentsID sont faux, alors oubliez ce qui précède et faites ceci:

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.
nda1983
la source
1

La plupart des réponses supposent que vous cherchez à faire cela en dehors de la base de données. Si vos arbres sont de nature relativement statique et que vous avez juste besoin de mapper les arbres dans la base de données, vous pouvez envisager d'utiliser des représentations d'ensemble imbriquées côté base de données. Consultez les livres de Joe Celko (ou ici pour un aperçu de Celko).

S'il est lié à Oracle dbs de toute façon, consultez leur CONNECT BY pour des approches SQL directes.

Dans les deux cas, vous pouvez ignorer complètement le mappage des arborescences avant de charger les données dans la base de données. Je pensais juste que je proposerais cela comme une alternative, cela peut être complètement inapproprié pour vos besoins spécifiques. L'ensemble de la partie "ordre correct" de la question initiale implique quelque peu que l'ordre soit "correct" dans la base de données pour une raison quelconque? Cela pourrait me pousser à manipuler les arbres là aussi.


la source
1

Ce n'est pas exactement la même chose que ce que le demandeur a recherché, mais j'ai eu du mal à comprendre les réponses formulées de manière ambiguë ici, et je pense toujours que cette réponse s'inscrit dans le titre.

Ma réponse est de mapper une structure plate sur une arborescence directement sur l'objet où tout ce que vous avez est un ParentIDsur chaque objet. ParentIDest nullou 0s'il s'agit d'une racine. En face du demandeur, je suppose que tout ParentIDle point de vue valide vers quelque chose d 'autre dans la liste:

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.
la source
1

voici une implémentation ruby:

Il cataloguera par nom d'attribut ou par le résultat d'un appel de méthode.

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
la source
1

La réponse acceptée me semble beaucoup trop complexe, j'ajoute donc une version Ruby et NodeJS de celle-ci

Supposons que la liste de nœuds plats ait la structure suivante:

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

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

Les fonctions qui transformeront la structure de liste plate ci-dessus en un arbre se présentent de la manière suivante

pour 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

pour 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
la source
Je crois que le chèque nulldoit être pourundefined
Ullauri le
@Ullauri null == undefined => truedans NodeJS
Hirurg103
1

une manière élégante de le faire est de représenter les éléments de la liste sous forme de chaîne contenant une liste de parents séparés par des points, et enfin une valeur:

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

Lors de l'assemblage d'un arbre, vous vous retrouveriez avec quelque chose comme:

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

J'ai une bibliothèque de configuration qui implémente cette configuration de remplacement (arborescence) à partir d'arguments de ligne de commande (liste). L'algorithme pour ajouter un seul élément à la liste dans un arbre est ici .

Omry Yadan
la source
0

Êtes-vous bloqué en utilisant uniquement ces attributs? Sinon, il peut être intéressant de créer un tableau de nœuds enfants, dans lequel vous pouvez parcourir tous ces objets une fois pour créer de tels attributs. À partir de là, sélectionnez le nœud avec des enfants mais pas de parents et construisez de manière itérative votre arbre de haut en bas.

Robert Elwell
la source
0

version 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
la source