HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Refactor Linq grouping

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
linqrefactorgrouping

Problem

All of the class here share these same property

Country, Total and Date.

Total property are integer in some classes and decimal in some classed.

The extension method below has a lot of duplication. i have tried to refactor it using interface but i can't find a way to refactor it at the last step on selection since it need different class

```
public static class TotalApprovedPOAmountStatisticExtension
{
public static void MergeNullAndEmplyCountry(this IEnumerable totalUserStatistic)
{
foreach (var item in totalUserStatistic)
{
if (string.IsNullOrWhiteSpace(item.Country))
{
item.Country = "";
}
}

totalUserStatistic = totalUserStatistic
.GroupBy(item => new { item.Country, item.Date })
.Select(groupedUser => new TotalApprovedPOAmountStatistic()
{ Country = groupedUser.Key.Country, Total = groupedUser.Sum(item => item.Total), Date = groupedUser.Key.Date }).ToList();

}
}

public static class TotalPOStatisticListExtension
{
public static void MergeNullAndEmplyCountry(this IEnumerable totalUserStatistic)
{
foreach (var item in totalUserStatistic)
{
if (string.IsNullOrWhiteSpace(item.Country))
{
item.Country = "";
}
}

totalUserStatistic = totalUserStatistic
.GroupBy(item => new { item.Country, item.Date })
.Select(groupedUser => new TotalPOStatistic() { Country = groupedUser.Key.Country, Total = groupedUser.Sum(item => item.Total), Date = groupedUser.Key.Date }).ToList();

}
}

public static class TotalProjectAmountStatisticListExtension
{
public static void MergeNullAndEmplyCountry(this IEnumerable totalUserStatistic)
{
foreach (var item in totalUserStatistic)
{
if (string.IsNullOrWhiteSpace(item

Solution

You can use an interface and a generic method to remove the code duplication:

public interface IStatistic
{
    string Country {get; set;}
    DateTime Date {get; set;}
    Decimal Total {get; set;}
}

public static class StatisticExtension
{
    public static void MergeNullAndEmplyCountry(this IEnumerable totalUserStatistic) where T : IStatistic, new()
    {
        foreach (var item in totalUserStatistic)
            if (string.IsNullOrWhiteSpace(item.Country))
                item.Country = "";

        totalUserStatistic = totalUserStatistic.GroupBy(item => new { item.Country, item.Date })
                                                .Select(groupedUser => new T() 
                                                { 
                                                    Country = groupedUser.Key.Country, 
                                                    Total = groupedUser.Sum(item => item.Total), 
                                                    Date = groupedUser.Key.Date 
                                                }).ToList();
    }
}


Important here is the new() constraint, which allows us to create an instance of T.

If the Total property of a class is int instead of decimal, we can make use of an explicit interface implementation:

public class BlaStatistic : IStatistic
{
    public string Country {get; set;}
    public DateTime Date {get; set;}
    public Int32 Total {get; set;}
    Decimal IStatistic.Total 
    {   
        get { return Convert.ToDecimal(Total); }
        set { Total = Convert.ToInt32(value); }
    }
}


Also, your extension method should actually return something, because it won't change the IEnumerable you pass in:

public static IEnumerable MergeNullAndEmplyCountry(this IEnumerable totalUserStatistic) where T : IStatistic, new()
{
    ...
    return totalUserStatistic.GroupBy...
}

Code Snippets

public interface IStatistic
{
    string Country {get; set;}
    DateTime Date {get; set;}
    Decimal Total {get; set;}
}

public static class StatisticExtension
{
    public static void MergeNullAndEmplyCountry<T>(this IEnumerable<T> totalUserStatistic) where T : IStatistic, new()
    {
        foreach (var item in totalUserStatistic)
            if (string.IsNullOrWhiteSpace(item.Country))
                item.Country = "";

        totalUserStatistic = totalUserStatistic.GroupBy(item => new { item.Country, item.Date })
                                                .Select(groupedUser => new T() 
                                                { 
                                                    Country = groupedUser.Key.Country, 
                                                    Total = groupedUser.Sum(item => item.Total), 
                                                    Date = groupedUser.Key.Date 
                                                }).ToList();
    }
}
public class BlaStatistic : IStatistic
{
    public string Country {get; set;}
    public DateTime Date {get; set;}
    public Int32 Total {get; set;}
    Decimal IStatistic.Total 
    {   
        get { return Convert.ToDecimal(Total); }
        set { Total = Convert.ToInt32(value); }
    }
}
public static IEnumerable<T> MergeNullAndEmplyCountry<T>(this IEnumerable<T> totalUserStatistic) where T : IStatistic, new()
{
    ...
    return totalUserStatistic.GroupBy...
}

Context

StackExchange Code Review Q#31674, answer score: 4

Revisions (0)

No revisions yet.