2

I have a linq query that gets ratings from the previous 12 months:

var ratingsByMonth =
    from month in Enumerable.Range(0, 12)
    let key = new { Year = DateTime.Now.AddMonths(-month).Year, Month = DateTime.Now.AddMonths(-month).Month }
    join groupedRating in ratings on key
            equals new
            {
                groupedRating.RatingDate.Year,
                groupedRating.RatingDate.Month
            } into g
    select new {
        Date = key,
        Rating = g.Select(x=>x.Rating)
    };

ratingsByMonth returns the following JSON from my data set:

{Date: {Year: 2016, Month: 11}, Rating: [4]}
{Date: {Year: 2016, Month: 10}, Rating: [5, 5, 4]}
{Date: {Year: 2016, Month: 9}, Rating: []}
{Date: {Year: 2016, Month: 8}, Rating: []}
{Date: {Year: 2016, Month: 7}, Rating: []}
{Date: {Year: 2016, Month: 6}, Rating: []}
{Date: {Year: 2016, Month: 5}, Rating: []}
{Date: {Year: 2016, Month: 4}, Rating: []}
{Date: {Year: 2016, Month: 3}, Rating: []}
{Date: {Year: 2016, Month: 2}, Rating: []}
{Date: {Year: 2016, Month: 1}, Rating: []}
{Date: {Year: 2015, Month: 12}, Rating: []}

However I'm trying to aggregate the Rating field into an Average. I've tried this:

return Json(new
{
    Average = ratingsByMonth.Average(x=>x.Rating),
    Dates = ratingsByMonth.Select(x => Date)
});

But I get the following error highlighted on the Average - line:

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'long?'

The output that I am looking for is:

{Date: {Year: 2016, Month: 11}, Rating: 4}
{Date: {Year: 2016, Month: 10}, Rating: 3.5}
{Date: {Year: 2016, Month: 9}, Rating: 0}
{Date: {Year: 2016, Month: 8}, Rating: 0}
{Date: {Year: 2016, Month: 7}, Rating: 0}
{Date: {Year: 2016, Month: 6}, Rating: 0}
{Date: {Year: 2016, Month: 5}, Rating: 0}
{Date: {Year: 2016, Month: 4}, Rating: 0}
{Date: {Year: 2016, Month: 3}, Rating: 0}
{Date: {Year: 2016, Month: 2}, Rating: 0}
{Date: {Year: 2016, Month: 1}, Rating: 0}
{Date: {Year: 2015, Month: 12}, Rating: 0}

Edit:

Have tried changing to the following:

return Json(new
{
    Average = ratingsByMonth.Select(x => x.Rating).SelectMany(x => x).Average(),
    Dates = ratingsByMonth.Select(x => x.Date.Month)
});

However, this just gives me a single 'Average' value across the whole data set, rather than an average per date.

Edit:

Here is sample data:

RatingID    Rating  RatingDate
5           5   2016-10-09 20:11:29.590
11          0   2016-10-09 20:14:42.503
21          5   2016-10-09 20:24:15.667
60          4   2016-10-28 11:01:21.220
64          4   2016-11-03 16:30:47.657

So for November the average should be 4, and October should be 3.5 (5+0+5+4 / 4)

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Evonet
  • 3,600
  • 4
  • 37
  • 83
  • There is a mismatch between the json and what you show in the linq. is it Date or GroupCriteria? Also show the desired output given the example data. I think your may be looking for a SelectMany and then Average – Nkosi Nov 29 '16 at 00:57
  • Thanks, have updated – Evonet Nov 29 '16 at 01:03

2 Answers2

1

In the original query you probably want to do this.

//other code removed for brevity
select new {
    Date = key,
    Rating = g.Count() > 0 ? g.Average(x => x.Rating) : 0
};

And then return the result

return Json(ratingsByMonth);
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Have tried this, but everytime I run an Average() at this point, I get Sequence contains no elements. Have tried adding .Where(x=x.Rating != 0) – Evonet Nov 29 '16 at 01:10
  • Ok. Can I see a small sample of ratings data – Nkosi Nov 29 '16 at 01:13
  • I've updated to show the exact output from ratingsByMonth and the desired output. Note that for Oct, ratingsByMonth is ignoring one of the 0 entries. – Evonet Nov 29 '16 at 01:24
  • Perfect! Thank you very much. – Evonet Nov 29 '16 at 01:34
0

The issue here is that the property Rating is itself a sequence, not a discrete value that you can ask the Average method to consider.

It sounds like you actually want to do something like: flatten all of the Rating collections into a single sequence and then take the average across all of the resulting values.

Something like:

var avg = ratingsByMonth.Select(x => x.Rating).SelectMany(x => x).Average()

This uses Select to produce a sequence of Rating arrays. SelectMany then flattens the list, after which you can take the average.

mtreit
  • 789
  • 4
  • 6
  • Thanks for looking, have updated my question with the results, wasn't quite what I was after. :-) – Evonet Nov 29 '16 at 01:07