nik
nik

Reputation: 1784

Conditional Groupby in LINQ

I m trying to do very similar linq statements but conditional on conditions they are slightly different. right now i just repeat the entire statement with small amendments but this should be possible much more concise. What I struggle with is to do the conditional groupby and also conditional select within the statement. My long version is:

class data
{
    public string year;
    public string quarter;
    public string month;
    public string week;
    public string tariff;
    public double volume;
    public double price;
}

class results
{
    public string product_code;
    public string tariff;
    public double volume;
    public double price;
}

class Program
{
    public static List<results> aggregationfunction(List<data> inputdata, string tarifftype, string timecategory)
    {
        List<results> returndata = new List<results>();

        if (tarifftype.Equals("daynight") & timecategory.Equals("yearly"))
        {
            returndata = inputdata.GroupBy(a => new { a.tariff, a.year })
                                  .Select(g => new results { product_code = g.Select(a => a.year).First(), tariff = g.Select(a => a.tariff).First(), volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
                                      .ToList();
        }
        else if (tarifftype.Equals("allday") & timecategory.Equals("yearly"))
        {
            returndata = inputdata.GroupBy(a => new { a.year })
                                  .Select(g => new results { product_code = g.Select(a => a.year).First(), tariff = "allday", volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
                                  .ToList();
        }
        else if (tarifftype.Equals("daynight") & timecategory.Equals("quarterly"))
        {
            returndata = = inputdata.GroupBy(a => new { a.tariff, a.year, a.quarter })
                                    .Select(g => new results { product_code = g.Select(a => a.year).First() + "_" + g.Select(a => a.quarter).First(), tariff = g.Select(a => a.tariff).First(), volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
                                    .ToList();
        }
        else if (tarifftype.Equals("allday") & timecategory.Equals("quarterly"))
        {
            returndata = inputdata.GroupBy(a => new { a.year, a.quarter })
                                  .Select(g => new results { product_code = g.Select(a => a.year).First() + "_" + g.Select(a => a.quarter).First(), tariff = "allday", volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
                                  .ToList();
        } 

        return returndata;
    }
}

Any pointers would be appreciated. As you can see the group by and allocation of tariff and product code differ but this shouldnt mean I need to repeat it all, does it?

Upvotes: 1

Views: 3965

Answers (2)

Ulugbek Umirov
Ulugbek Umirov

Reputation: 12797

Shorter code:

return inputdata
    .GroupBy(a => new
    {
        a.year,
        quarter = timecategory == "quarterly" ? a.quarter : string.Empty,
        tariff = tarifftype == "daynight" ? a.tariff : "allday"
    })
    .Select(g => new results
    {
        product_code = g.Key.year + (string.IsNullOrEmpty(g.Key.quarter) ? "" : "_" + g.Key.quarter),
        tariff = g.Key.tariff,
        volume = g.Sum(a => a.volume),
        price = g.Average(a => a.price)
    })
    .ToList();

Upvotes: 9

nmclean
nmclean

Reputation: 7724

The IGrouping<TKey, data> items returned by inputdata.GroupBy (where data is the element type of inputdata) will always implement IEnumerable<data> regardless of the anonymous key type (which may be either {year} or {tariff, year} etc). So all the GroupBy values could be generalized to IEnumerable<IEnumerable<data>> and you could then break it down to two steps:

IEnumerable<IEnumerable<data>> groups;
if (condition) {
    groups = inputdata.GroupBy(a => new { a.year });
} else if (condition) {
    groups = inputdata.GroupBy(a => new { a.tariff, a.year });
} else {
    groups = inputdata.GroupBy(a => new { a.year, a.quarter });
}
returndata = groups.Select(...)

The conditional select should be simpler to implement because you can just use conditional operators inline, or expand the selector function to a multi-line block, e.g.:

.Select(g => {
    var product_code = ...;
    if (condition) {
        product_code += "_" + ...;
    }
    return new results { product_code = product_code, ... };
})

Upvotes: 1

Related Questions