I am trying to find a way to cut out repeated code in an application that center around LINQ select statements. Lets say we have existing table rows that need to be aggregated and grouped for different reporting requirements and all original data is just grouped by day and needs to be grouped by week / month and another property.
DataRow is an example object that needs to be grouped and converted into an object ReportTableRow (please note this is a much reduced object but the actual objects have far more properties and therefore become much more drawn out).
public class DataRow
{
public DateTime Date { get; set; }
public string AccountNumber { get; set; }
public string MachineNumber { get; set; }
public int TEST { get; set; }
}
public class ReportTableRow
{
public int WeekNumber { get; set; }
public int Month { get; set; }
public string AccountNumber{ get; set; }
public string MachineNumber { get; set; }
public int TEST { get; set; }
public string TEST_TRAFFICLIGHT { get; set; }
}
And we create a list of DataRows:
List<DataRow> reportTable = new List<DataRow>()
{
new DataRow()
{
Date = new DateTime(2021, 06, 14),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 2
},
new DataRow()
{
Date = new DateTime(2021, 06, 15),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 1
},
new DataRow()
{
Date = new DateTime(2021, 06, 15),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 6
},
new DataRow()
{
Date = new DateTime(2021, 06, 16),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 4
},
new DataRow()
{
Date = new DateTime(2021, 06, 17),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 2
},
new DataRow()
{
Date = new DateTime(2021, 06, 18),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 7
},
new DataRow()
{
Date = new DateTime(2021, 06, 19),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 2
},
new DataRow()
{
Date = new DateTime(2021, 06, 20),
AccountNumber = "11111111",
MachineNumber = "00AB2021",
TEST = 11
},
new DataRow()
{
Date = new DateTime(2021, 06, 14),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 2
},
new DataRow()
{
Date = new DateTime(2021, 06, 15),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 1
},
new DataRow()
{
Date = new DateTime(2021, 06, 15),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 6
},
new DataRow()
{
Date = new DateTime(2021, 06, 16),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 4
},
new DataRow()
{
Date = new DateTime(2021, 06, 17),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 2
},
new DataRow()
{
Date = new DateTime(2021, 06, 18),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 7
},
new DataRow()
{
Date = new DateTime(2021, 06, 19),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 2
},
new DataRow()
{
Date = new DateTime(2021, 06, 20),
AccountNumber = "22222222",
MachineNumber = "11BC2021",
TEST = 11
}
};
So if we either need the data grouped "BY WEEK" or "BY MONTH" then we need to actually return in the report the WeekNumber or Month number respectively and the grouping would look something like this where GetTrafficLight method returns a string value based on the value of the sum of TEST:
switch (aggregate.ToUpper())
{
case "BY WEEK":
reportTable = reportTable
.GroupBy(x => new { x.AccountNumber, WeekNumber = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(x.Date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday) })
.Select(x => new ReportTableRow
{
WeekNumber = x.Key.WeekNumber,
Month = x.Max(y => y.Date).Month,
MachineNumber = x.FirstOrDefault().MachineNumber,
TEST = x.Sum(y => y.TEST),
TEST_TRAFFICLIGHT = GetTrafficLight(x.Sum(y => y.TEST)
})
.ToList();
break;
case "BY MONTH":
reportTable = reportTable
.GroupBy(x => new { x.AccountNumber, x.Date.Month })
.Select(x => new ReportTableRow
{
WeekNumber = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(x.Max(y => y.Date), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday),
Month = x.Key.Month,
MachineNumber = x.FirstOrDefault().MachineNumber,
TEST = x.Sum(y => y.TEST),
TEST_TRAFFICLIGHT = GetTrafficLight(x.Sum(y => y.TEST)
})
.ToList();
break;
}
The question is, is there anyway to remove the "Select" code and pass it into either a method or object that would accept an anonymous grouping so that it can be reused multiple times. Changing the anonymous grouping to a compile time object that contains two properties means duplicates are then returned in the dataset, could be that finding a way to remove the duplicates in compile time grouped members might help to resolve?
Please note, the code has been created to pose this question.