Bagaimana cara efisien membangun pohon dari struktur datar?

153

Saya memiliki banyak objek dalam struktur datar. Benda-benda ini memiliki IDdan ParentIDproperti sehingga mereka dapat diatur di pohon. Mereka tidak dalam urutan tertentu. Setiap ParentIDproperti tidak harus cocok dengan IDdalam struktur. Oleh karena itu mereka mungkin beberapa pohon yang muncul dari benda-benda ini.

Bagaimana Anda memproses objek-objek ini untuk membuat pohon yang dihasilkan?

Saya tidak jauh dari solusi tapi saya yakin itu masih jauh dari optimal ...

Saya perlu membuat pohon ini untuk kemudian memasukkan data ke dalam database, dalam urutan yang benar.

Tidak ada referensi melingkar. Node adalah RootNode ketika ParentID == null atau ketika ParentID tidak dapat ditemukan di objek lain

Costo
sumber
Apa yang Anda maksud dengan "buat"? Render di UI? Simpan secara hierarkis dalam XML atau database?
RedFilter
Bagaimana Anda mendefinisikan simpul tanpa orang tua (yaitu simpul root). ParentID adalah nol? ParentID = 0? Saya menganggap tidak ada referensi melingkar yang benar?
Jason Punyon
5
Saya menemukan pertanyaan ini cukup keren.
nes1983
1
lihat artikel ini: scip.be/index.php?Page=ArticlesNET23&Lang=NL
ebram khalil

Jawaban:

120

Menyimpan ID dari objek dalam pemetaan tabel hash ke objek tertentu. Hitung semua objek dan temukan induknya jika ada dan perbarui pointer induknya.

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
sumber
5
bahasa apa itu? (Saya ambil C #)
Jason S
3
Algo ini adalah (dalam notasi informal) O (3N), di mana sebagai solusi O (1N) mudah dicapai dengan baik instantiating Nodes parsial untuk orang tua yang tidak 'dilintasi' ATAU dengan menjaga tabel pencarian sekunder untuk anak-anak non-instantiated orangtua. Mungkin tidak masalah untuk sebagian besar penggunaan di dunia nyata, tetapi bisa jadi signifikan pada set data besar.
Andrew Hanlon
15
@AndrewHanlon mungkin Anda harus memposting sol untuk 0 (1N)
Ced
1
@Ced Martin Schmidt jawaban di bawah ini sangat dekat dengan bagaimana saya akan mengimplementasikannya. Seperti dapat dilihat, ia menggunakan satu loop dan sisanya adalah operasi hashtable.
Andrew Hanlon
26
O (3N) hanya O (N);)
JakeWilson801
34

Berdasarkan jawaban Mehrdad Afshari dan komentar Andrew Hanlon untuk sebuah speedup, inilah pendapat saya.

Perbedaan penting dengan tugas asli: Simpul root memiliki 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
sumber
1
Bagus, ini pada dasarnya pendekatan yang saya maksudkan. Namun saya hanya akan menggunakan simpul root pseudo (dengan ID = 0 dan null Induk) dan menghapus persyaratan referensi diri.
Andrew Hanlon
Satu-satunya hal yang hilang dalam contoh ini adalah menetapkan bidang Induk ke dalam setiap simpul anak. Untuk melakukannya, kita hanya perlu mengatur bidang Induk setelah menambahkan anak-anak ke Koleksi Induknya. Seperti itu: parentNode.Children.Add (ourNode); ourNode.Parent = parentNode;
plauriola
@plauriola Benar, terima kasih, saya menambahkan ini. Alternatifnya adalah dengan hanya menghapus properti Induk, itu tidak perlu untuk algoritma inti.
Martin Schmidt
4
Karena saya tidak dapat menemukan modul npm yang mengimplementasikan solusi O (n), saya membuat yang berikut (unit diuji, cakupan kode 100%, hanya berukuran 0,5 kb dan termasuk pengetikan. Mungkin ini membantu seseorang: npmjs.com/package / performant-array-to-tree
Philip Stanislaus
32

Berikut adalah algoritma JavaScript sederhana untuk mem-parsing tabel datar ke struktur pohon induk / anak yang berjalan dalam waktu N:

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
sumber
mencoba mengubah pendekatan ini ke C #.
hakan
menyadari bahwa jika id dimulai dari sesuatu yang besar seperti 1001 maka kita mendapatkan indeks di luar pengecualian ...
hakan
2
Kiat: gunakan console.log(JSON.stringify(root, null, 2));untuk mencetak hasil cetak dengan cantik.
aloisdg pindah ke codidact.com
14

Solusi python

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

Sebagai contoh:

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

Menghasilkan:

{
    "1": {
        "4": {
            "10": {}, 
            "11": {
                "16": {}, 
                "17": {
                    "24": {}, 
                    "25": {}
                }
            }
        }, 
        "5": {
            "8": {}, 
            "9": {
                "20": {}, 
                "12": {
                    "22": {}, 
                    "23": {
                        "2": {}, 
                        "27": {}, 
                        "26": {}
                    }
                }, 
                "21": {}, 
                "7": {}
            }
        }
    }
}
minkwe
sumber
Hai. Bagaimana cara menambahkan atribut lain dalam output? yaitu. name, parent_id
pria sederhana
sejauh ini yang paling elegan!
ccpizza
@simpleguy: pemahaman daftar dapat dibuka jika Anda membutuhkan lebih banyak kontrol, misalnya: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

Versi JS yang mengembalikan satu root atau array root yang masing-masing akan memiliki properti array Kids yang berisi anak-anak terkait. Tidak bergantung pada input yang dipesan, padat kompak, dan tidak menggunakan rekursi. Nikmati!

// 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
sumber
2
Pertanyaan ini berusia 7 tahun dan sudah memiliki jawaban yang dipilih dan diterima. Jika Anda pikir Anda memiliki solusi yang lebih baik, akan sangat bagus untuk menambahkan beberapa penjelasan ke kode Anda.
Jordi Nebot
Pendekatan ini bekerja dengan baik untuk tipe data yang tidak teratur ini.
Cody C
4

Temukan versi JavaScript yang mengagumkan di sini: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Katakanlah Anda memiliki array seperti ini:

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

Dan Anda ingin memiliki objek bersarang seperti ini:

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

Inilah fungsi rekursif yang membuatnya terjadi.

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

Bahasa:

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
sumber
Untuk setiap parentId itu mengulang seluruh model - bukankah ini O (N ^ 2)?
Ed Randall
4

Bagi siapa pun yang tertarik dengan versi C # dari solusi Eugene, perhatikan bahwa node_list diakses sebagai peta, jadi gunakan Kamus saja.

Perlu diingat bahwa solusi ini hanya berfungsi jika tabel diurutkan berdasarkan 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]);
}

Node didefinisikan sebagai berikut.

class Node
{
    public int id { get; set; }
    public int parent_id { get; set; }
    public List<Node> children = new List<Node>();
}
Joel Malone
sumber
1
Itu terlalu tua tetapi Item Daftar 8 new Node { parent_id = 7, id = 9 },mencegah node_list.Add(item.id, item);untuk menyelesaikan karena Kunci tidak dapat diulang; itu salah ketik; jadi, alih-alih id = 9 , ketik id = 8
Marcelo Scofano
Tetap. Terima kasih @MarceloScofano!
Joel Malone
3

Saya menulis solusi generik dalam C # secara longgar berdasarkan jawaban @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
sumber
Turun pemilih, silakan komentar. Saya akan senang mengetahui kesalahan saya.
HuBeZa
2

Berikut adalah solusi java dari jawaban oleh 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;
    }
}
Bhimal Vimal
sumber
Anda harus menjelaskan sedikit apa ide Anda di balik kode.
Ziad Akiki
Ini hanya terjemahan Jawa dari jawaban sebelumnya
Vimal Bhatt
1

Samar-samar karena pertanyaannya bagi saya, saya mungkin akan membuat peta dari ID ke objek yang sebenarnya. Di pseudo-java (saya tidak memeriksa apakah ia berfungsi / mengkompilasi), itu mungkin seperti:

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

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

Dan untuk mencari setiap orang tua:

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

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

Dengan menggunakan kembali getRealObject(ID)dan melakukan peta dari objek ke koleksi objek (atau ID mereka), Anda mendapatkan peta orangtua-anak-anak juga.

Henrik Paul
sumber
1

Saya dapat melakukan ini dalam 4 baris kode dan O (n log n) waktu, dengan asumsi bahwa Kamus adalah sesuatu seperti 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, dan sekarang saya membaca bahwa beberapa parentID palsu, jadi lupakan yang di atas, dan lakukan ini:

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
sumber
1

Sebagian besar jawaban menganggap Anda ingin melakukan ini di luar basis data. Jika pohon Anda relatif statis dan Anda hanya perlu memetakan pohon-pohon tersebut ke dalam basis data, Anda mungkin ingin mempertimbangkan untuk menggunakan representasi kumpulan bersarang di sisi basis data. Lihatlah buku-buku karya Joe Celko (atau di sini untuk ikhtisar oleh Celko).

Jika dikaitkan dengan Oracle dbs, periksa CONNECT BY mereka untuk pendekatan SQL langsung.

Dengan pendekatan mana pun, Anda bisa melewatkan pemetaan pohon sebelum memuat data ke dalam basis data. Hanya berpikir saya akan menawarkan ini sebagai alternatif, mungkin benar-benar tidak sesuai untuk kebutuhan spesifik Anda. Seluruh bagian "urutan yang tepat" dari pertanyaan awal agak menyiratkan Anda memerlukan perintah untuk menjadi "benar" di db karena alasan tertentu? Ini mungkin mendorong saya ke arah penanganan pohon di sana juga.


sumber
1

Ini tidak persis sama dengan apa yang dicari si penanya, tetapi saya mengalami kesulitan membungkus kepala saya di sekitar jawaban yang diuraikan secara ambigu yang disediakan di sini, dan saya masih berpikir jawaban ini sesuai dengan judulnya.

Jawaban saya adalah untuk memetakan struktur datar ke pohon objek langsung di mana semua yang Anda miliki adalah ParentIDpada setiap objek. ParentIDadalah nullatau 0jika itu adalah root. Berlawanan dengan penanya, saya menganggap semua ParentIDpoin valid untuk hal lain dalam daftar:

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.
sumber
1

di sini adalah implementasi ruby:

Ini akan katalog berdasarkan nama atribut atau hasil pemanggilan metode.

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
sumber
1

Jawaban yang diterima terlihat terlalu rumit bagi saya, jadi saya menambahkan versi Ruby dan NodeJS

Misalkan daftar node datar memiliki struktur berikut:

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

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

Fungsi-fungsi yang akan mengubah struktur daftar datar di atas menjadi pohon terlihat dengan cara berikut

untuk 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

untuk 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
sumber
Saya percaya cek untuk nullkebutuhanundefined
Ullauri
@Ullauri null == undefined => truedi NodeJS
Hirurg103
1

salah satu cara elegan untuk melakukan ini adalah untuk mewakili item dalam daftar sebagai string yang memegang daftar titik yang dipisahkan dari orang tua, dan akhirnya nilai:

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

Saat merakit pohon, Anda akan berakhir dengan sesuatu seperti:

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

Saya memiliki pustaka konfigurasi yang mengimplementasikan konfigurasi timpa ini (pohon) dari argumen baris perintah (daftar). Algoritma untuk menambahkan satu item ke daftar ke pohon ada di sini .

Omry Yadan
sumber
0

Apakah Anda hanya menggunakan atribut tersebut? Jika tidak, mungkin menyenangkan untuk membuat array node anak, di mana Anda dapat menggilir semua objek ini sekali untuk membangun atribut seperti itu. Dari sana, pilih simpul dengan anak-anak tetapi tidak ada orang tua dan iteratif membangun pohon Anda dari atas ke bawah.

Robert Elwell
sumber
0

versi 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
sumber