Làm thế nào để xây dựng cây hiệu quả từ một cấu trúc phẳng?


153

Tôi có một loạt các đối tượng trong một cấu trúc phẳng. Những đồ vật này có một IDvà một ParentIDtài sản để chúng có thể được sắp xếp trong cây. Họ không theo thứ tự đặc biệt. Mỗi thuộc ParentIDtính không nhất thiết phải khớp với một IDtrong cấu trúc. Do đó, chúng có thể là một số cây nổi lên từ những vật thể này.

Làm thế nào bạn sẽ xử lý các đối tượng này để tạo ra cây kết quả?

Tôi không ở quá xa một giải pháp nhưng tôi chắc chắn rằng nó không phải là tối ưu ...

Tôi cần tạo các cây này để chèn Dữ liệu vào cơ sở dữ liệu theo đúng thứ tự.

Không có tài liệu tham khảo tròn. Một nút là một RootNode khi ParentID == null hoặc khi không thể tìm thấy ParentID trong các đối tượng khác


"Tạo" nghĩa là gì? Kết xuất trong giao diện người dùng? Lưu trữ theo kiểu phân cấp trong XML hoặc cơ sở dữ liệu?
RedFilter

Làm thế nào để bạn xác định một nút không có cha mẹ (tức là một nút gốc). ParentID là null? ParentID = 0? Tôi giả sử không có tài liệu tham khảo tròn đúng?
Jason Punyon

5
Tôi thấy câu hỏi này khá tuyệt.
nes1983

Câu trả lời:


119

Lưu trữ ID của các đối tượng trong ánh xạ bảng băm đến đối tượng cụ thể. Liệt kê thông qua tất cả các đối tượng và tìm cha mẹ của chúng nếu nó tồn tại và cập nhật con trỏ cha của nó cho phù hợp.

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
ngôn ngữ đó là gì? (Tôi lấy nó C #)
Jason S

3
Thuật toán này là (theo ký hiệu không chính thức) O (3N), trong đó giải pháp O (1N) có thể dễ dàng đạt được bằng cách tạo ra các Nút một phần cho cha mẹ không đi qua HOẶC bằng cách giữ một bảng tra cứu thứ cấp cho trẻ em không được lập tức cha mẹ. Có thể không quan trọng đối với hầu hết các sử dụng trong thế giới thực, nhưng nó có thể có ý nghĩa trên các tập dữ liệu lớn.
Andrew Hanlon

15
@AndrewHanlon có lẽ bạn nên đăng sol với giá 0 (1N)
Ced

1
Câu trả lời dưới đây của @Ced Martin Schmidt rất gần với cách tôi sẽ thực hiện nó. Có thể thấy, nó sử dụng một vòng lặp duy nhất và phần còn lại là các hoạt động có thể băm.
Andrew Hanlon

26
O (3N) chỉ là O (N);)
JakeWilson801

34

Dựa trên câu trả lời của Mehrdad Afshari và nhận xét của Andrew Hanlon về việc tăng tốc, đây là ý kiến ​​của tôi.

Sự khác biệt quan trọng đối với tác vụ gốc: Một nút gốc có 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
Thật tuyệt, đây cơ bản là cách tiếp cận mà tôi đã ám chỉ. Tuy nhiên, tôi sẽ chỉ sử dụng một nút gốc giả (với ID = 0 và null Parent) và loại bỏ yêu cầu tự tham chiếu.
Andrew Hanlon

Điều duy nhất còn thiếu trong ví dụ này là gán trường Parent vào mọi nút con. Để làm như vậy, chúng ta chỉ cần đặt trường Cha mẹ sau khi thêm con vào Bộ sưu tập Cha mẹ. Giống như vậy: ParentNode.Children.Add (ourNode); ourNode.Parent = ParentNode;
plauriola

@plauriola Đúng, cảm ơn, tôi đã thêm cái này. Một cách khác là chỉ cần loại bỏ thuộc tính Parent, không cần thiết cho thuật toán cốt lõi.
Martin Schmidt

4
Vì tôi không thể tìm thấy mô-đun npm thực hiện giải pháp O (n), tôi đã tạo một mô-đun sau (đơn vị được kiểm tra, độ bao phủ mã 100%, kích thước chỉ 0,5 kb và bao gồm các kiểu chữ. Có thể nó giúp ai đó: npmjs.com/package / Performanceant-Array-to-tree
Philip Stanislaus

32

Đây là một thuật toán JavaScript đơn giản để phân tích một bảng phẳng thành cấu trúc cây cha / con chạy trong thời gian 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);

cố gắng chuyển đổi cách tiếp cận này sang C #.
hakan

nhận ra rằng nếu id bắt đầu từ một cái gì đó lớn như 1001 thì chúng ta sẽ lấy chỉ số ra khỏi ngoại lệ bị ràng buộc ...
hakan

2
Mẹo: sử dụng console.log(JSON.stringify(root, null, 2));để in đẹp đầu ra.
aloisdg chuyển đến codidact.com

14

Giải pháp Python

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

Ví dụ:

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

Sản xuất:

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

Chào. Làm thế nào để tôi thêm một thuộc tính khác trong đầu ra? I E. tên, Parent_id
anh chàng đơn giản

cho đến nay thanh lịch nhất!
ccpizza

@simpleguy: việc hiểu danh sách có thể được mở ra trong trường hợp bạn cần kiểm soát nhiều hơn, ví dụ: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

Phiên bản JS trả về một gốc hoặc một mảng gốc, mỗi gốc sẽ có thuộc tính mảng Children chứa các con liên quan. Không phụ thuộc vào đầu vào có trật tự, nhỏ gọn và không sử dụng đệ quy. Thưởng thức!

// 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
Câu hỏi này đã 7 tuổi và đã có một câu trả lời được bình chọn và chấp nhận. Nếu bạn nghĩ rằng bạn có một giải pháp tốt hơn, thật tuyệt vời khi thêm một số lời giải thích vào mã của bạn.
Jordi Nebot

Cách tiếp cận này hoạt động tốt cho loại dữ liệu không có thứ tự này.
Cody C

4

Tìm thấy một phiên bản JavaScript tuyệt vời ở đây: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Giả sử bạn có một mảng như thế này:

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

Và bạn muốn có các đối tượng lồng nhau như thế này:

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

Đây là một hàm đệ quy làm cho nó xảy ra.

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

Sử dụng:

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

Đối với mọi phụ huynh, nó lặp lại toàn bộ mô hình - đây có phải là O (N ^ 2) không?
Ed Randall

4

Đối với bất kỳ ai quan tâm đến phiên bản C # của giải pháp của Eugene, hãy lưu ý rằng node_list được truy cập dưới dạng bản đồ, vì vậy hãy sử dụng Từ điển thay thế.

Hãy nhớ rằng giải pháp này chỉ hoạt động nếu bảng được sắp xếp bởi 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]);
}

Nút được định nghĩa như sau.

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

1
Nó quá cũ nhưng Mục Danh sách 8 new Node { parent_id = 7, id = 9 },ngăn không node_list.Add(item.id, item);hoàn thành vì Khóa không thể lặp lại; đó là một lỗi đánh máy; vì vậy, thay vì id = 9 , hãy nhập id = 8
Marcelo Scofano

Đã sửa. Cảm ơn @MarceloScofano!
Joel Malone

3

Tôi đã viết một giải pháp chung trong C # một cách lỏng lẻo dựa trên câu trả lời @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;
  }
}

Xuống cử tri, xin vui lòng bình luận. Tôi sẽ rất vui khi biết những gì tôi đã làm sai.
HuBeZa

2

Đây là giải pháp java của câu trả lời của 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;
    }
}

Bạn nên giải thích một chút ý tưởng của bạn đằng sau mã.
Ziad Akiki

Đây chỉ là bản dịch Java của câu trả lời trước đó
Vimal Bhatt

1

Mơ hồ như câu hỏi đối với tôi, có lẽ tôi sẽ tạo một bản đồ từ ID đến đối tượng thực tế. Trong pseudo-java (Tôi không kiểm tra xem nó có hoạt động / biên dịch không), nó có thể giống như:

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

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

Và để tìm kiếm từng phụ huynh:

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

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

Bằng cách sử dụng lại getRealObject(ID)và thực hiện một bản đồ từ đối tượng đến một bộ sưu tập các đối tượng (hoặc ID của chúng), bạn cũng có được bản đồ cha mẹ-> trẻ em.


1

Tôi có thể làm điều này trong 4 dòng mã và thời gian O (n log n), giả sử rằng Dictionary là một cái gì đó giống như 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, và bây giờ tôi đọc được rằng một số ParentID là giả mạo, vì vậy hãy quên những điều trên và làm điều này:

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

Hầu hết các câu trả lời cho rằng bạn đang muốn làm điều này bên ngoài cơ sở dữ liệu. Nếu cây của bạn có bản chất tương đối tĩnh và bạn chỉ cần ánh xạ cây vào cơ sở dữ liệu bằng cách nào đó, bạn có thể muốn xem xét sử dụng các biểu diễn tập hợp lồng nhau ở phía cơ sở dữ liệu. Kiểm tra sách của Joe Celko (hoặc ở đây để biết tổng quan về Celko).

Nếu được liên kết với Oracle dbs, hãy xem CONNECT BY của họ để biết các cách tiếp cận SQL thẳng.

Với bất kỳ cách tiếp cận nào, bạn hoàn toàn có thể bỏ qua việc ánh xạ các cây trước khi bạn tải dữ liệu lên trong cơ sở dữ liệu. Chỉ cần nghĩ rằng tôi sẽ cung cấp điều này như là một thay thế, nó có thể hoàn toàn không phù hợp với nhu cầu cụ thể của bạn. Toàn bộ phần "thứ tự đúng" của câu hỏi ban đầu phần nào ngụ ý rằng bạn cần thứ tự "chính xác" trong db vì một số lý do? Điều này có thể đẩy tôi về phía xử lý các cây ở đó là tốt.


1

Nó không giống hệt như những gì người hỏi tìm kiếm, nhưng tôi gặp khó khăn trong việc xoay quanh những câu trả lời mơ hồ được cung cấp ở đây, và tôi vẫn nghĩ rằng câu trả lời này phù hợp với tiêu đề.

Câu trả lời của tôi là để ánh xạ một cấu trúc phẳng vào cây đối tượng trực tiếp, nơi tất cả những gì bạn có là ParentIDtrên mỗi đối tượng. ParentIDnullhoặc 0nếu nó là một root. Đối diện với người hỏi, tôi giả sử tất cả các điểm hợp lệ ParentIDđối với một cái gì đó khác trong danh sách:

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

đây là một triển khai ruby:

Nó sẽ lập danh mục theo tên thuộc tính hoặc kết quả của một cuộc gọi phương thức.

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

Câu trả lời được chấp nhận có vẻ quá phức tạp đối với tôi vì vậy tôi đang thêm phiên bản Ruby và NodeJS của nó

Giả sử rằng danh sách các nút phẳng có cấu trúc sau:

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

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

Các hàm sẽ biến cấu trúc danh sách phẳng ở trên thành một cái cây theo cách sau

cho 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

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

};

Tôi tin rằng việc kiểm tra các nullnhu cầu cần phải cóundefined
Ullauri

@Ullauri null == undefined => truetrong NodeJS
Hirurg103

1

Một cách thanh lịch để làm điều này là biểu diễn các mục trong danh sách dưới dạng chuỗi giữ danh sách cha mẹ được phân tách bằng dấu chấm và cuối cùng là một giá trị:

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

Khi lắp ráp một cây, bạn sẽ kết thúc với một cái gì đó như:

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

Tôi có một thư viện cấu hình thực hiện cấu hình ghi đè (cây) này từ các đối số dòng lệnh (danh sách). Thuật toán để thêm một mục duy nhất vào danh sách vào cây có ở đây .


0

Bạn có bị mắc kẹt khi chỉ sử dụng các thuộc tính đó? Nếu không, có thể tốt hơn khi tạo một mảng các nút con, nơi bạn có thể quay vòng qua tất cả các đối tượng này một lần để xây dựng các thuộc tính như vậy. Từ đó, chọn nút có con nhưng không có cha mẹ và lặp lại xây dựng cây của bạn từ trên xuống.


0

phiên bản 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());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.