Search code examples
c#linq

LINQ Group inside a group


I have a datatable like this:

date Type Agency TotalCount ABC_Count DEF_Count GHI_Count
JAN-2022 X B2X 5 5 2 3
JAN-2022 X C4A 7 5 7 2
FEB-2022 X B2X 3 2 3 1
FEB-2022 X C4A 9 1 9 4
MAR-2022 X B2X 8 3 1 8
MAR-2022 X C4A 7 1 1 7
JAN-2022 Y D5Y 6 6 4 3
JAN-2022 Y E7T 7 3 7 2
FEB-2022 Y D5Y 4 2 4 1
FEB-2022 Y E7T 9 2 9 4
MAR-2022 Y D5Y 8 3 1 8
MAR-2022 Y E7T 8 1 1 8

Code:

public class GroupModel {
    public string LetterGroupName { get; set; }
    public List<LetterGroupData> LetterGroupData { get; set; }
}

public class LetterGroupData {
    public string Date { get; set; }
    public double TypeX_Ave { get; set; }
    public double TypeY_Ave { get; set; }
    public int Total { get; set; }
}

My desired output would be a list of GroupModel List<GroupModel> and it should be something like this:

"GroupModel" : 
[
{
    "LetterGroupName" : "ABC",
    "LetterGroupData" : [
    {
         "Date": "JAN-2022",
         "TypeX_Ave":  40.0, //Sum ABC_Count for Type X / (Sum of TotalCount for JAN-2022 Type X + Y)
         "TypeY_Ave": 36.0,//Sum ABC_Count for Type Y / (Sum of TotalCount for JAN-2022 Type X + Y)
         "Total": 25 //sum of TotalCount for JAN-2022 Type X + Y
    },
    {
         "Date": "FEB-2022",
         "TypeX_Ave":  12.0, //Sum ABC_Count for Type X / (Sum of TotalCount for FEB-2022 Type X + Y)
         "TypeY_Ave": 16.0,//Sum ABC_Count for Type Y / (Sum of TotalCount for FEB-2022 Type X + Y)
         "Total": 25 //sum of TotalCount for FEB-2022 Type X + Y
    },//..and so on for MAR-2022
    ]
},
{
    "LetterGroupName" : "DEF",
    "LetterGroupData" : [
    {
         "Date": "JAN-2022",
         "TypeX_Ave":  36.0, //Sum DEF_Count for Type X / (Sum of TotalCount for JAN-2022 Type X + Y)
         "TypeY_Ave": 44.0,//Sum DEF_Count for Type Y / (Sum of TotalCount for JAN-2022 Type X + Y)
         "Total": 25 //sum of TotalCount for JAN-2022 Type X + Y
    },
    {
         "Date": "FEB-2022",
         "TypeX_Ave":  48.0, //Sum DEF_Count for Type X / (Sum of TotalCount for FEB-2022 Type X + Y)
         "TypeY_Ave": 52.0,//Sum DEF_Count for Type Y / (Sum of TotalCount for FEB-2022 Type X + Y)
         "Total": 25 //sum of TotalCount for FEB-2022 Type X + Y
    },//..and so on for MAR-2022
    ]
},//...and so on for LetterGroupName GHI
]

How do I achieve this result using only a single LINQ query?


Solution

  • Implementation:

    private static IReadOnlyCollection<GroupModel> GetGroups(IReadOnlyCollection<Dictionary<string, object>> input) =>
        (
            from groupName in new[] { "ABC", "DEF", "GHI" }
            select
                new GroupModel
                {
                    LetterGroupName = groupName,
                    LetterGroupData = (
                        from item in input
                        group item by (string)item["date"] into itemGroup
                        let total = itemGroup.Sum(item => (int)item["TotalCount"])
                        select new LetterGroupData
                        {
                            Date = itemGroup.Key,
                            TypeX_Ave = (double)itemGroup
                                .Where(item => (string)item["Type"] == "X")
                                .Sum(item => (int)item[groupName + "_Count"])
                                / total * 100,
                            TypeY_Ave = (double)itemGroup
                                .Where(item => (string)item["Type"] == "Y")
                                .Sum(item => (int)item[groupName + "_Count"])
                                / total * 100,
                            Total = total,
                        }).ToList()
                }
        ).ToList();
    
    

    Usage:

    var input =
        new Dictionary<string, object>[]
        {
            new()
            {
                ["date"] = "JAN-2022",
                ["Type"] = "X",
                ["Agency"] = "B2X",
                ["TotalCount"] = 5,
                ["ABC_Count"] = 5,
                ["DEF_Count"] = 2,
                ["GHI_Count"] = 3,
            },
            new()
            {
                ["date"] = "JAN-2022",
                ["Type"] = "X",
                ["Agency"] = "C4A",
                ["TotalCount"] = 7,
                ["ABC_Count"] = 5,
                ["DEF_Count"] = 7,
                ["GHI_Count"] = 2,
            },
            new()
            {
                ["date"] = "JAN-2022",
                ["Type"] = "Y",
                ["Agency"] = "B2X",
                ["TotalCount"] = 6,
                ["ABC_Count"] = 6,
                ["DEF_Count"] = 4,
                ["GHI_Count"] = 3,
            },
            new()
            {
                ["date"] = "JAN-2022",
                ["Type"] = "Y",
                ["Agency"] = "C4A",
                ["TotalCount"] = 7,
                ["ABC_Count"] = 3,
                ["DEF_Count"] = 7,
                ["GHI_Count"] = 2,
            },
            // ...
        };
    
    var groups = GetGroups(input);
    // groups.Dump();
    

    .NET Fiddle