I have following function to generate a table shown in the output screenshot. I want to modify this function to get desired output shown below but I am not very familiar with C#. If I pass years array into this function that consists of 3 items, how should I modify this function?
private string GenerateHTML(string year)
{
var regions = RegionalList(year);
if (!regions.Any()) return "";
var rowHtmlString = regions.OrderByDescending(row => row.Capacity).Aggregate("", (current, row) => current + $@"
<tr>
<td>
<p>{row.Region}</p>
</td>
<td>
<p>{row.Percent.ToString("F1")}%</p>
</td>
</tr>
");
return rowHtmlString;
}
So your method RegionalList
is a method with input a string Year, and output a sequence of Regions. Apparently every Region has a Descriptive Name
(North, East, etc), and a value Capacity
. Something like this:
class Region
{
public string Name {get; set;} // may be enum
public decimal Capacity {get; set;}
... // other properties
}
IEnumerable<Region> GetRegions(string year) {...} // Formerly known as ListRegions
You already found a method to create HTML if you have only one year. Now you have an input sequence of years, and you want the output as Region Names with per year the capacity
, something like this:
class YearCapacity
{
public int Year {get; set;}
public decimal Capacity {get; set;}
}
class RegionWithItsYearCapacities
{
public string RegionName {get; set;}
public ICollection<YearCapacity> YearCapacities {get; set;}
}
Once you've got your sequence of Regions with their YearCapacities, I guess you know how to convert this to the proper HTML. My advice would be to separate this conversion, so if later you want to convert to XML, or JSON, or any other format, you don't need to change the original LINQ.
So I assume you have the following:
// converts one Region with its year capacities into HTML:
static string ToHtml(this RegionWithItsYearCapacities regionWithItsYearCapacities)
{
... // TODO: implement. Outside of this question
}
I use an extension method for this. If you are not familiar with extension methods, consider to read Extension Methods Demystified
And the extension method to convert a sequence into HTML:
static <string ToHtml (
this IEnumerable<RegionWithItsYearCapacities> regionsWithTheirYearCapacities)
{
// if needed add preliminary HTML
string result = ...
// add HTML for each region with its own YearCapacities
foreach (var region in regionsWithTheirYearCapacities)
{
result += region.ToHtml();
}
// if needed add some post HTML:
result += ...
return string;
}
The reason why I made this method, is because I'm not sure if it is good HTML to only have a repeating sequence of information about regions with their year capacities. Maybe you want add some preliminary / post information. (Well, the real reason is that I'm not sure about proper HTML formatting)
Ok, here is the code:
IEnumerable<string> years = ...
IEnumerable<Region> yearWithRegions = years.Select(year => new
{
Year = year,
Regions = GetRegions(year),
});
Now you've got a sequence of Years, and with every Year, you've got all Regions in this year, something like this:
2022:
North: 8.9%
East: 36.8%
South: 10.4%
West: 18.5%
2021:
North: 8.0%
East: 0.8%
South: 0.4%
West: 18.5%
2020:
etc.
We'll flatten this sequence into into a sequence of Year - Region - Capacity
, then make groups of Regions, each Region with its Year - Capacity
.
Flattening is done using one of the overloads of Enumerable.SelectMany. Grouping is done using Enumerable.GroupBy
var RegionsWithTheirYearCapacities = yearWithRegions // see intermediate result above
// Flatten:
.SelectMany(yearWithRegion => yearWithRegion.Name,
yearWithRegion => yearWithRegion.Regions, // the collection to flatten
// parameter Result Selector: from every year and Region-Capacity combination
// make one new:
(year, regionCapacityCombination) => new
{
Year = year,
Region = regionCapacityCombination.Name,
Capacity = regionCapacityCombination.Capacity),
})
Wait, not finished yet, I just want to show what we've got now:
2022 - North - 8.9%
2022 - East - 36.8%
2022 - South - 10.4%
2022 - West - 18.5%
2021 - North - 8.0%
2021 - East - 0.8%
2021 - South - 0.4%
2021 - West - 18.5%
2020 - ... etc
Ok, continuing the LINQ, make groups of Year - Region - Capacity combinations that have the same value for Region:
.GroupBy(yearRegionCapacityCombination => yearRegionCapacityCombination.Region
// parameter resultSelector: for every found Region, with all combinations
// that have this Region, make one new:
(region, combinationsWithThisRegion) => new RegionWithItsYearCapacities
{
Region = region,
YearCapacities = combinationsWithThisRegion.Select(combination => new YearCapacity
{
Year = combination.Year,
Capacity = combination.Capacity,
})
.ToList(),
})
Intermediate result:
North:
2022: 8.9%
2021: 8.0%
2020: ...
East:
2022: 36.8%
2021: 0.8%
2020: ...
South: ... etc
Almost there, all we have to do is convert this to a sequence of HTML:
.ToHtml();
To make clear to you what happens, I clipped the LINQ into smaller pieces. Of course you can put it all in one big LINQ. This won' change efficiency. However I'm sure it will deteriorate readability, reuse, changeability and unit test possibilities.