I have table ReportData having below records
Agency | Year | Month | MonthCount |
---|---|---|---|
One | 2017 | Jan | 4 |
One | 2017 | Feb | 6 |
One | 2017 | Nov | 29 |
One | 2017 | Dec | 38 |
One | 2018 | Jan | 20 |
One | 2019 | Feb | 18 |
Two | 2017 | Jan | 24 |
Two | 2017 | Feb | 66 |
Three | 2017 | Nov | 9 |
Four | 2017 | Dec | 8 |
Four | 2018 | Jan | 22 |
Four | 2019 | Feb | 48 |
how to convert into below table? please let me know the Linq statement. Thank you for your help in advance.
Agency | Month | 2017 | 2018 | 2019 |
---|---|---|---|---|
One | Jan | 4 | 20 | 0 |
One | Feb | 6 | 0 | 18 |
One | Nov | 29 | 0 | 0 |
One | Dec | 38 | 0 | 0 |
Two | Jan | 24 | 0 | 0 |
Two | Feb | 66 | 0 | 0 |
Three | Nov | 9 | 0 | 0 |
Four | Jan | 0 | 22 | 0 |
Four | Feb | 0 | 0 | 48 |
Four | Dec | 8 | 0 | 0 |
source class structure
public class ReportingData
{
public string AgencyName { get; set; }
public int Year { get; set; }
public string MonthNumnber { get; set; }
public string Month { get; set; }
public int MonthCount { get; set; }
}
CodePudding user response:
It seems to me that per [Agency, Month] combination, you want the MonthCounts of years 2017..2019.
I wonder what you want in a few years: only the last three years? or years 2017.. 2025? But that is a later problem.
My advice would be to make Groups of ReportData with same value of [Agency, Month] combination. Use the overload of Enumerable.GroupBy that has a parameter resultSelector to precisely define the result. If your data is in a different process (usually a database), use the IQueryable version.
IEnumerable<Report> reportData = ...
// make groups with same values for [Agency, Month] combination
var result = reportData.GroupBy(report => new
{
Agency = report.Agency,
Month = report.Month,
},
// parameter resultSelector: for every [Agency, Month] combination,
// and all reports that have this [Agency, Month] combination,
// make one new:
(agencyMonthCombination, reports] => new
{
Agency = agencyMonthCombination.Agency,
Month = agencyMonthCombination.Month,
Year2017 = reports.Where(report => report.Year == 2017)
.Select(report => report.MonthCount)
.Sum();
Year2018 = reports.Where(report => report.Year == 2018)
.Select(report => report.MonthCount)
.Sum();
Year2019 = reports.Where(report => report.Year == 2018)
.Select(report => report.MonthCount)
.Sum();
If you expect only one report per year, you don't have to Sum, you can just use FirstOrDefault.
You see the disadvantage of this approach of naming the years 2017..2019. Code would be much easier if you made it for all years, or the last N years:
// parameter resultSelector
(agencyMonthCombination, reports] => new
{
Agency = agencyMonthCombination.Agency,
Month = agencyMonthCombination.Month,
// group the reports with this [Agency, Month] combination in years
MonthCounts = reports.GroupBy(report => report.Year,
(year, reportsInThisYear) => new
{
Year = year,
MonthCount = reportsInThisYear.Select(report => report.MonthCount).Sum(),
})
// if you only want the last N: orderby descending Year and Take(N)
.OrderByDescending(yearMonthCount => yearMonthCount.Year)
.Take(N)
.ToList(),
});