I have the following model
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; }
}
i have this sample list populated using the models above
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");
}
}
Rule #1: only entry which is not a group has cost values entered manually by user while the group entry cost is calculated Rule #2: nesting of groups are allowed.
what i want is to do hierarchical summation for each group as shown in the table below the value inside the square brackets has to be calculated.
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
Thanks in Advance
I've managed finally to have the answer
first you need to group element by parent
var groups = entries.ToLookup(x => x.ParentId).ToDictionary(x => x.Key ?? 0, x
=> x.ToArray().Select(e => e.Id).ToList());
then get all children helpers
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);
}
}
}
Then Iterate over the list to populate the totals
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;
});
}
}
});
}
}