Как хранить иерархичные данные в списке

93
30 мая 2021, 08:10

Есть класс. Данные я получаю из Excel файла, пробегаясь по разным листам. Данные есть - теперь надо понять - как построить из них иерархию. Как вообще можно хранить иерархичные данные в списке?

public class DB
{
    public string Code { get; set; }
    public string  Name { get; set; }
    public string Id { get; set; }
    public string ParentId { get; set; }
}
Answer 1

Допустим есть класс

public class DB
{
    public string Code { get; set; }
    public string Name { get; set; }
    public string Id { get; set; }
    public string ParentId { get; set; }
}

Мы можем хранить его в словаре

Dictionary<string, DB> store = new Dictionary<string, DB> ();   

Заполним словарь рандомно

var r = new Random();   
for(int i=0; i<100; i++)
{
    var db = new DB {
    Code = $"code {i}",
        Id = i.ToString(),
        Name = $"name {i}",
        ParentId = i %  (r.Next(99)+1) == 0 ? null : r.Next(100).ToString()
    };
    store.Add(db.Id, db);       
}

Поиск родителей - тривиальное занятие

public int GetCountOfParents(Dictionary<string, DB> store, string id)
{
    var parents = new HashSet<string>();
    var q = new Queue<string>();    
    q.Enqueue(id);  
    while(q.Count>0)
    {
        var item = q.Dequeue();
        var parent = store[item].ParentId;
        if (!string.IsNullOrEmpty(parent) && parents.Add(parent)) q.Enqueue(parent);
    }   
    return parents.Count;
}

Пробежаться по всем элементам еще проще

foreach (var item in store.Values)
    Console.WriteLine($"{item.Id} has {GetCountOfParents(store, item.Id)} parents");

Вывод закономерен

0 has 0 parents
1 has 1 parents
2 has 11 parents
3 has 0 parents
4 has 5 parents
5 has 1 parents
.....
91 has 10 parents
92 has 5 parents
93 has 4 parents
94 has 8 parents
95 has 8 parents
96 has 5 parents
97 has 10 parents
98 has 11 parents
99 has 9 parents

Если интересны прямо переходы между парентами, можно добавить вот такую функцию

public IEnumerable<DB> GetParents(Dictionary<string, DB> store, string id)
{
    var parents = new HashSet<string>();
    var q = new Queue<string>();
    q.Enqueue(id);
    while (q.Count > 0)
    {
        var item = q.Dequeue();
        var parent = store[item].ParentId;
        if (!string.IsNullOrEmpty(parent) && parents.Add(parent))
        {
            yield return store[parent];
            q.Enqueue(parent);
        }
    }   
}

Перечислить все вот так

foreach (var item in store.Values)
    Console.WriteLine($"{item.Id} has parents {GetParents(store, item.Id).Aggregate("", (x, y) => $"{x} => {y.Id}")} ");

На выходе получим

0 has parents  
1 has parents  => 27 => 77 => 19 => 48 => 72 => 2 => 85 => 71 => 59 => 30 => 95 => 58 
2 has parents  => 85 => 71 => 59 => 30 => 95 => 58 
3 has parents => 69 => 54 => 44 => 2 => 85 => 71 => 59 => 30 => 95 => 58
4 has parents => 12 => 39 => 75 => 49 => 14 => 86 => 67 => 61
5 has parents => 57 => 99 => 4 => 12 => 39 => 75 => 49 => 14 => 86 => 67 => 61
6 has parents => 62 => 5 => 57 => 99 => 4 => 12 => 39 => 75 => 49 => 14 => 86 => 67 => 61
7 has parents => 54 => 44 => 2 => 85 => 71 => 59 => 30 => 95 => 58
8 has parents => 13 => 41 => 68 => 37 => 89 => 1 => 27 => 77 => 19 => 48 => 72 => 2 => 85 => 71 => 59 => 30 => 95 => 58
9 has parents => 69 => 54 => 44 => 2 => 85 => 71 => 59 => 30 => 95 => 58
10 has parents => 32 => 6 => 62 => 5 => 57 => 99 => 4 => 12 => 39 => 75 => 49 => 14 => 86 => 67 => 61
11 has parents => 56
.......
91 has parents => 82 => 22 => 85 => 71 => 59 => 30 => 95 => 58
92 has parents => 41 => 68 => 37 => 89 => 1 => 27 => 77 => 19 => 48 => 72 => 2 => 85 => 71 => 59 => 30 => 95 => 58
93 has parents => 84 => 44 => 2 => 85 => 71 => 59 => 30 => 95 => 58
94 has parents
95 has parents => 58
96 has parents => 6 => 62 => 5 => 57 => 99 => 4 => 12 => 39 => 75 => 49 => 14 => 86 => 67 => 61
97 has parents => 43 => 46
98 has parents => 65 => 59 => 30 => 95 => 58
99 has parents => 4 => 12 => 39 => 75 => 49 => 14 => 86 => 67 => 61

Ну и имейте ввиду, что я при генерации совсем не следил за ацикличностью.

UPD

Более простая версия для получения родителей, даже очереди не надо

public IEnumerable<DB> GetParents(Dictionary<string, DB> store, string id)
{
    var parents = new HashSet<string>();    
    id = store[id].ParentId;
    while (!string.IsNullOrEmpty(id) && parents.Add(id))
    {       
        yield return store[id];
        id = store[id].ParentId;
    }
}
READ ALSO
Как сложить два многочлена разной длины

Как сложить два многочлена разной длины

В общем, есть функция с перегруженным оператором сложенияЕсли степень многочленов одинаковая, то всё считает спокойно, но если степень многочленов...

97
Как правильно использовать SecureString?

Как правильно использовать SecureString?

Например, нужно захешировать пароль методом string CalculateHash(string source)Но если я вытяну значение строки с SecureString в виде экземпляра класса string и передам...

102
Когда и как включается DI в asp netcore

Когда и как включается DI в asp netcore

В описаниях DI для asp netcore пишут, что настройка выполняется в ConfigureServices() класса Startup

92
Покраска Excel при экспорте

Покраска Excel при экспорте

нужно покрасить определенные столбцы в другой цвет, но проблема в том, что он красит не только столбцы, но и все ячейки до конца файла, как...

91