Иерархическое суммирование родительского ребенка в C# - PullRequest
0 голосов
/ 27 марта 2020

У меня есть следующая модель

   class Entry
{
    public int Id { get; set; }

    public bool IsGroup { get; set; }

    public int? ParentId { get; set; }

    public List<YearCost> YearCost { get; set; } = new List<YearCost>();
}

class YearCost
{
    public int Year { get; set; }
    public decimal Cost { get; set; }
}

У меня есть этот пример списка, заполненный с использованием моделей выше

    static void Main(string[] args)
    {

        var entries = new List<Entry> {
             new Entry
             {
                 Id = 1,
                 ParentId = null, 
                 IsGroup = true,
             },
             new Entry
             {
                 Id = 2,
                 ParentId = 1,
                 IsGroup = false,
                 YearCost = new List<YearCost> {
                 new YearCost { Year = 2019, Cost = 10 },
                 new YearCost { Year = 2020, Cost = 10 }
                 }
             },
             new Entry
             {
                 Id = 3,
                 ParentId = 1,
                 IsGroup = true
             },
             new Entry
             {
                 Id = 4,
                 ParentId = 3,
                 IsGroup = true
             },
             new Entry
             {
                 Id = 5,
                 ParentId = 4,
                 IsGroup = false,
                 YearCost = new List<YearCost> {
                 new YearCost { Year = 2019, Cost = 15 },
                 new YearCost { Year = 2020, Cost = 10 }
                 }
             },
             new Entry
             {
                 Id = 6,
                 ParentId = 4,
                 IsGroup = false,
                 YearCost = new List<YearCost> {
                 new YearCost { Year = 2019, Cost = 15 },
                 new YearCost { Year = 2020, Cost = 10 }
                 }
             },
             new Entry
             {
                 Id = 7,
                 ParentId = 3,
                 IsGroup = true
             },
             new Entry
             {
                 Id = 8,
                 ParentId = 7,
                 IsGroup = false,
                 YearCost = new List<YearCost> {
                 new YearCost { Year = 2019, Cost = 30 },
                 new YearCost { Year = 2020, Cost = 30 }
                 }
             },
             new Entry
             {
                 Id = 9,
                 ParentId = 7,
                 IsGroup = false,
                 YearCost = new List<YearCost> {
                 new YearCost { Year = 2019, Cost = 20 },
                 new YearCost { Year = 2020, Cost = 20 }
                 }
             },
             new Entry
             {
                 Id = 10,
                 ParentId = 3,
                 IsGroup = false,
                 YearCost = new List<YearCost> {
                 new YearCost { Year = 2019, Cost = 5 },
                 new YearCost { Year = 2020, Cost = 5 }
                 }
             },
        }; 

        Console.WriteLine(String.Format("{0,10}{1,10}{2,10}{3, 10}{4, 10}", "Id", "Group", "Parent Id", 2019, 2020));
        Console.WriteLine(String.Format("{0,10}{1,10}{2,10}{3, 10}{4, 10}", "--", "-----", "---------", "----", "----"));
        foreach (var entry in entries.OrderBy(x=>x.ParentId))
        {
            Console.Write(String.Format("{0,10}{1,10}{2,10}", entry.Id, entry.IsGroup ? "yes" : "no", entry.ParentId?.ToString() ?? "NULL", 2019, 2020));
            foreach (var y in entry.YearCost)
                Console.Write(String.Format("{0,10}", y.Cost));
            Console.WriteLine("\n");
        }
    }

Правило # 1: только запись, которая не является группой, имеет значения стоимости вводится пользователем вручную при расчете стоимости входа в группу. Правило № 2: допускается вложение групп.

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

    Id     Group    Parent Id    2019      2020
    --     -----    ---------    ----      ----
     1       yes       NULL      [95]      [85]
     2       no          1        10        10 
     3       yes         1       [85]      [75]
     4       yes         3       [30]      [20]
     7       yes         3       [50]      [50]
    10       no          3         5        5
     5       no          4        15        10
     6       no          4        15        10
     8       no          7        30        30
     9       no          7        20        20

Заранее спасибо

1 Ответ

0 голосов
/ 20 апреля 2020

Мне наконец удалось получить ответ

сначала вам нужно сгруппировать элемент по родителю

  var groups = entries.ToLookup(x => x.ParentId).ToDictionary(x => x.Key ?? 0, x 

  => x.ToArray().Select(e => e.Id).ToList());

, затем получите всех детей-помощников

private List<int> GetAllChildren(int? parent, Dictionary<int, List<int>> groups)
    { 
        List<int> children = new List<int>();
        PopulateChildren(parent, children, groups);
        return children;
    }

    private void PopulateChildren(int? parent, List<int> children, Dictionary<int, List<int>> groups)
    {
        List<int> myChildren;
        if (groups.TryGetValue(parent.Value, out myChildren))
        {
            children.AddRange(myChildren);
            foreach (int child in myChildren)
            {
                PopulateChildren(child, children, groups);
            }
        }
    }

Затем выполните итерацию по списку, чтобы заполните итоги

foreach (var item in entries)
{
    if (item.IsGroup)
    {
        var children = GetAllChildren(item.Id, groups);
        children.ForEach(c => {
            var entry = entries.FirstOrDefault(x => x.Id == c);
            if(entry != null)
            {
                if (!item.isGroup)
                {
                    entry.YearCosts?.ForEach(y =>
                    {
                            if (item.YearCosts.FirstOrDefault(yx => yx.Year == y.Year) == null)
                            {
                                item.YearCosts.Add(new YearCost { Year = y.Year, Cost = 0 });
                            }
                            item.YearCosts.FirstOrDefault(yx => yx.Year == y.Year).Cost += y.Cost ?? 0;
                            item.SubTotal += y.Cost ?? 0;
                    });
                }
            } 
        }); 
    }
}
...