Düz bir yapıdan verimli bir ağaç nasıl yapılır?


164

Düz bir yapıda bir sürü nesnem var. Bu nesnelerin bir IDve bir ParentIDözelliği vardır, bu nedenle ağaçlara yerleştirilebilirler. Belirli bir sıraya sahip değiller. Her ParentIDözellik ID, yapıdaki bir ile eşleşmek zorunda değildir . Bu nedenle, bu nesnelerden çıkan birkaç ağaç olabilir.

Ortaya çıkan ağaçları oluşturmak için bu nesneleri nasıl işlersiniz?

Bir çözümden çok uzak değilim ama eminim ki optimal olmaktan uzaktır ...

Daha sonra Verileri bir veritabanına doğru sırayla eklemek için bu ağaçları oluşturmam gerekiyor.

Döngüsel referans yoktur. Bir Düğüm, ParentID == null olduğunda veya diğer nesnelerde ParentID bulunamadığında bir RootNode'dur.


"Yaratmak" derken neyi kastediyorsunuz? Bir kullanıcı arayüzünde oluşturulsun mu? XML'de veya bir veritabanında hiyerarşik bir şekilde depolamak mı?
RedFilter

Üst öğesi olmayan bir düğümü (yani kök düğümü) nasıl tanımlarsınız? ParentID null? ParentID = 0? Doğru döngüsel referans olmadığını varsayıyorum?
Jason Punyon

8
Bu soruyu oldukça havalı buluyorum.
nes1983

1
bu makaleye göz atın
ebram khalil

Yanıtlar:


124

Nesnelerin kimliklerini bir karma tablo eşlemesinde belirli bir nesneye depolayın. Tüm nesneleri numaralandırın ve varsa üst öğelerini bulun ve buna göre üst işaretçisini güncelleyin.

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
bu hangi dil (C # olarak alıyorum)
Jason S

3
Bu algo, (gayri resmi gösterimde) O (3N) 'dir, burada bir O (1N) çözümü,' geçmeyen 'ebeveynler için kısmi Düğümler başlatarak VEYA somutlaştırılmamış çocukları için ikincil bir arama tabloları tutarak kolayca elde edilebilir. ebeveynler. Muhtemelen çoğu gerçek dünya kullanımı için önemli değildir, ancak büyük veri kümelerinde önemli olabilir.
Andrew Hanlon

17
@AndrewHanlon belki solu 0 (1N) için yayınlamalısın
Ced

1
@Ced Martin Schmidt'in aşağıdaki cevabı, onu nasıl uygulayacağıma çok yakın. Görüldüğü gibi tek bir döngü kullanıyor ve geri kalanı hashtable işlemlerinden oluşuyor.
Andrew Hanlon

27
O (3N) sadece O (N);)
JakeWilson801

36

Mehrdad Afshari'nin cevabına ve Andrew Hanlon'un hızlandırma yorumuna dayanarak, işte benim alacağım.

Orijinal görevle önemli fark: Bir kök düğümün ID == parentID'si vardır.

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
Güzel, temelde bahsettiğim yaklaşım buydu. Bununla birlikte, sadece bir sahte kök düğümü (ID = 0 ve null Parent ile) kullanır ve öz referans gereksinimini kaldırırdım.
Andrew Hanlon

Bu örnekte eksik olan tek şey, Ana alanı her çocuk düğümüne atamaktır. Bunu yapmak için, sadece çocukları Ebeveyn Koleksiyonuna ekledikten sonra Ebeveyn alanını ayarlamamız gerekir. Şöyle: parentNode.Children.Add (bizimNode'umuz); ourNode.Parent = parentNode;
plauriola

@plauriola Doğru, teşekkürler, bunu ekledim. Bir alternatif, sadece Parent özelliğini kaldırmak olabilir, çekirdek algoritma için gerekli değildir.
Martin Schmidt

5
O (n) çözümünü uygulayan bir npm modülü bulamadığım için aşağıdakini oluşturdum (test edilen birim,% 100 kod kapsamı, sadece 0,5 kb boyutunda ve yazı içeriyor. Belki birine yardımcı olur: npmjs.com/package / performant-array-to-tree
Philip Stanislaus

33

Düz bir tabloyu N zaman içinde çalışan bir üst / alt ağaç yapısına ayrıştırmak için basit bir JavaScript algoritması:

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

bu yaklaşımı C # 'a dönüştürmeye çalışıyorum.
hakan

id 1001 gibi büyük bir şeyden başlıyorsa, endeksi sınır dışı istisnadan çıkardığımızı fark etti ...
hakan

3
İpucu: console.log(JSON.stringify(root, null, 2));çıktıyı güzel yazdırmak için kullanın .
aloisdg,

15

Python çözümü

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

Örneğin:

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

Üretir:

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

Selam. Çıktıya başka bir özniteliği nasıl eklerim? yani. name, parent_id
simple guy

açık ara en zarif!
ccpizza

@simpleguy: Daha fazla kontrole ihtiyaç duymanız durumunda liste anlayışı açılabilir, örneğin: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

Her biri ilgili alt öğeleri içeren bir Çocuk dizisi özelliğine sahip olacak bir kök veya bir kök dizisi döndüren JS sürümü. Sıralı girdiye bağlı değildir, düzgün kompakttır ve özyineleme kullanmaz. Zevk almak!

// 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
Bu soru 7 yaşında ve zaten oylanmış ve kabul edilmiş bir cevabı var. Daha iyi bir çözümünüz olduğunu düşünüyorsanız, kodunuza biraz açıklama eklemek harika olur.
Jordi Nebot

Bu yaklaşım, bu sırasız veri türü için işe yarar.
Cody C

4

Burada harika bir JavaScript sürümü buldum : http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Diyelim ki böyle bir diziniz var:

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

Ve nesnelerin şu şekilde iç içe olmasını istiyorsunuz:

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

İşte bunu gerçekleştiren özyinelemeli bir işlev.

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

Kullanım:

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

Her parentId için tüm modeli döngüye sokuyor - bu O (N ^ 2) değil mi?
Ed Randall

4

Eugene'nin çözümünün C # sürümüyle ilgilenen herkes için node_list'e harita olarak erişildiğini unutmayın , bu nedenle bunun yerine Sözlük kullanın.

Bu çözümün yalnızca tablonun parent_id'ye göre sıralanması durumunda çalıştığını unutmayın .

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

Düğüm aşağıdaki gibi tanımlanır.

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

1
Çok eski ancak Liste Öğesi 8 tamamlanmayı new Node { parent_id = 7, id = 9 },engelliyor node_list.Add(item.id, item);çünkü Anahtar tekrarlayamıyor; bu bir yazım hatası; bu nedenle, yerine id = 9 , tip id = 8
Marcelo Scofano

Sabit. Teşekkürler @MarceloScofano!
Joel Malone

3

@Mehrdad Afshari cevabına dayalı olarak C # ile genel bir çözüm yazdım:

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

Aşağı oy veren, lütfen yorum yapın. Neyi yanlış yaptığımı bilmekten mutlu olacağım.
HuBeZa

3

İşte Mehrdad Afshari'nin cevabının java çözümü.

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

Kodun arkasındaki fikrinizin ne olduğunu biraz açıklamalısınız.
Ziad Akiki

Bu, önceki cevabın sadece Java çevirisi
Vimal Bhatt

1

Soru bana belirsiz görünse de, muhtemelen kimlikten gerçek nesneye bir harita oluştururdum. Sözde java'da (çalışıp çalışmadığını / derlenip çalışmadığını kontrol etmedim), şöyle bir şey olabilir:

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

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

Ve her ebeveyne bakmak için:

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

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

Nesneden getRealObject(ID)bir nesne koleksiyonuna (veya bunların kimliklerine) bir harita yeniden kullanarak ve yaparak, bir üst> alt harita da elde edersiniz.


1

Dictionary'nin TreeMap gibi bir şey olduğunu varsayarak bunu 4 satır kod ve O (n log n) zamanında yapabilirim.

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.

DÜZENLEME : Tamam ve şimdi bazı ebeveyn kimliklerinin sahte olduğunu okudum, bu yüzden yukarıdakileri unutun ve şunu yapın:

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

Cevapların çoğu, bunu veritabanının dışında yapmak istediğinizi varsayar. Ağaçlarınız doğası gereği nispeten durağansa ve ağaçları bir şekilde veritabanına eşlemeniz gerekiyorsa, veritabanı tarafında iç içe küme temsillerini kullanmayı düşünebilirsiniz. Joe Celko'nun kitaplarına göz atın (veya Celko'nun genel bakışını burada bulabilirsiniz).

Yine de Oracle dbs'ye bağlıysa, doğrudan SQL yaklaşımları için CONNECT BY'a bakın.

Her iki yaklaşımla da, verileri veritabanına yüklemeden önce ağaçların eşlemesini tamamen atlayabilirsiniz. Bunu bir alternatif olarak sunacağımı düşündüm, özel ihtiyaçlarınız için tamamen uygun olmayabilir. Orijinal sorunun tüm "uygun düzen" kısmı, bir şekilde veritabanında "doğru" olması için sıranın bir şekilde gerekli olduğunu gösteriyor? Bu beni oradaki ağaçlara dokunmaya da itebilir.


1

Soruyu soranın aradığı şeyle tam olarak aynı değil, ama burada verilen belirsiz bir şekilde ifade edilmiş cevapları kafamı dolamakta güçlük çektim ve yine de bu cevabın başlığa uyduğunu düşünüyorum.

Cevabım, sahip olduğunuz ParentIDher şeyin her bir nesne üzerinde olduğu, düz bir yapıyı doğrudan nesne üzerindeki bir ağaca eşlemek için . ParentIDolduğu nullya 0da bir kök olup olmadığını. Soruyu soranın karşısında, tüm geçerli ParentIDkişilerin listedeki başka bir şeye işaret ettiğini varsayıyorum :

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

İşte bir Ruby uygulaması:

Öznitelik adına veya bir yöntem çağrısının sonucuna göre kataloglayacaktır.

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

Kabul edilen yanıt benim için çok karmaşık görünüyor, bu yüzden onun Ruby ve NodeJS versiyonlarını ekliyorum

Düz düğüm listesinin aşağıdaki yapıya sahip olduğunu varsayalım:

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

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

Yukarıdaki düz liste yapısını ağaca dönüştürecek fonksiyonlar şu şekildedir:

Ruby için:

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

NodeJS için:

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)

};

Çekin olması nullgerektiğine inanıyorumundefined
Ullauri

@Ullauri null == undefined => truein NodeJS
Hirurg103

1

Bunu yapmanın zarif bir yolu, listedeki öğeleri noktayla ayrılmış üst öğe listesi ve son olarak bir değer içeren bir dize olarak temsil etmektir:

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

Bir ağacı monte ederken, aşağıdaki gibi bir şey elde edersiniz:

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

Bu geçersiz kılma yapılandırmasını (ağacı) komut satırı bağımsız değişkenlerinden (liste) uygulayan bir yapılandırma kitaplığım var. Listeye bir ağaca tek bir öğe ekleme algoritması buradadır .


0

Sadece bu nitelikleri mi kullanıyorsunuz? Değilse, bu tür nitelikleri oluşturmak için tüm bu nesneler arasında bir kez dolaşabileceğiniz bir dizi alt düğüm oluşturmak güzel olabilir. Oradan, çocuklu düğümü seçin, ancak ebeveynleri yok ve ağacınızı yukarıdan aşağıya yinelemeli olarak oluşturun.


0

java versiyonu

// 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());
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.