(ID / ParentID) список в иерархический список - PullRequest
12 голосов
/ 23 февраля 2012

MyClass состоит из ID ParentID и List<MyClass> как Children

У меня есть список MyClass, как это

ID  ParentID
1   0
2   7
3   1
4   5
5   1
6   2
7   1
8   6
9   0
10  9

Вывод (иерархический список) как List<MyClass>

1 __ 3
 |__ 5__ 4
 |__ 7__ 2__ 6__ 8
     |__ 11

9 __10

Какой самый простой способ добиться этого в linq?
P.S .: ParentID не отсортировано

Edit:
Моя попытка:

class MyClass
{
    public int ID;
    public int ParentID;
    public List<MyClass> Children = new List<MyClass>();
    public MyClass(int id, int parent_id)
    {
        ID = id;
        ParentID = parent_id;
    }
}

инициализировать пример данных и попытаться получить иерархические данные

 List<MyClass> items = new List<MyClass>()
{
    new MyClass(1, 0), 
    new MyClass(2, 7), 
    new MyClass(3, 1), 
    new MyClass(4, 5), 
    new MyClass(5, 1), 
    new MyClass(6, 2), 
    new MyClass(7,1), 
    new MyClass(8, 6), 
    new MyClass(9, 0), 
    new MyClass(10, 9), 
    new MyClass(11, 7), 
};

Dictionary<int, MyClass> dic = items.ToDictionary(ee => ee.ID);

foreach (var c in items)
    if (dic.ContainsKey(c.ParentID))
        dic[c.ParentID].Children.Add(c);

как видите, много слов, которые мне не нужны, все еще в словаре

Ответы [ 3 ]

38 голосов
/ 23 января 2013

Рекурсия здесь не требуется, если вы строите отношения родитель-потомок перед фильтрацией.Поскольку члены списка остаются одними и теми же объектами, при условии, что вы связываете каждого члена списка с его непосредственными потомками, все необходимые отношения будут построены.

Это можно сделать в две строки:

items.ForEach(item => item.Children = items.Where(child => child.ParentID == item.ID)
                                           .ToList());
List<MyClass> topItems = items.Where(item => item.ParentID == 0).ToList();
16 голосов
/ 25 февраля 2012

Для иерархических данных вам нужна рекурсия - цикла foreach будет недостаточно.

Action<MyClass> SetChildren = null;
SetChildren = parent =>
    {
        parent.Children = items
            .Where(childItem => childItem.ParentID == parent.ID)
            .ToList();

        //Recursively call the SetChildren method for each child.
        parent.Children
            .ForEach(SetChildren);
    };

//Initialize the hierarchical list to root level items
List<MyClass> hierarchicalItems = items
    .Where(rootItem => rootItem.ParentID == 0)
    .ToList();

//Call the SetChildren method to set the children on each root level item.
hierarchicalItems.ForEach(SetChildren);

items - это тот же список, который вы используете. Обратите внимание, как метод SetChildren вызывается внутри себя. Это то, что строит иерархию.

1 голос
/ 01 апреля 2014

Мне потребовались такие функциональные возможности, и я сравнил оба метода и нашел, что метод 2-й быстрее, чем 1-й :), сейчас в моей базе данных карты или записи ограничены, но 1-й метод занимает в 4 раза больше времени.

Может быть, это может помочь тем, кто осознает время.

1 метод


    public JsonResult CardData()
    {
        var watch = System.Diagnostics.Stopwatch.StartNew();
        OrgChartWithApiContext db = new OrgChartWithApiContext();

        var items = db.Cards.ToList();
        Action<Card> SetChildren = null;
        SetChildren = parent => {
            parent.Children = items
                .Where(childItem => childItem.ParentId == parent.id)
                .ToList();

            //Recursively call the SetChildren method for each child.
            parent.Children
                .ForEach(SetChildren);
        };

        //Initialize the hierarchical list to root level items
        List<Card> hierarchicalItems = items
            .Where(rootItem => !rootItem.ParentId.HasValue)
            .ToList();

        //Call the SetChildren method to set the children on each root level item.
        hierarchicalItems.ForEach(SetChildren);
        watch.Stop();
        var timetaken = watch.ElapsedMilliseconds;

        return new JsonResult() { Data = hierarchicalItems, ContentType = "Json", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
    }

метод 2


    public JsonResult Card2Data()
    {
        var watch = System.Diagnostics.Stopwatch.StartNew();
        OrgChartWithApiContext db = new OrgChartWithApiContext();
        var items = db.Cards.ToList();
        List<Card> topItems = items.Where(item => !item.ParentId.HasValue).ToList();
        topItems.ForEach(item => item.Children = items.Where(child => child.ParentId == item.id).ToList());
        watch.Stop();
        var timetaken = watch.ElapsedMilliseconds;
        return new JsonResult() { Data = topItems, ContentType = "Json", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
    }
...