user3113376 user3113376 - 11 months ago 50
C# Question

Getting average value of groups with LINQ

I am trying to split my List into different groups based on a certain value each item in the List has, and then find the average of another value in those groups.

To better put it, I have a list of Students:

List<Student> students = new List<Student> {
new Student { Name="Bob", Level=Level.Sophomore, GPA=3.2f },
new Student { Name="Cathy", Level=Level.Freshman, GPA=3.6f },
new Student { Name="James", Level=Level.Senior, GPA=3.8f },
new Student { Name="Jessica", Level=Level.Senior, GPA=3.7f },
new Student { Name="Derek", Level=Level.Junior, GPA=2.8f },
new Student { Name="Sam", Level=Level.Junior, GPA=3.1f }
};


And I want to group them by their Class Level, so they'll be grouped into Freshman, Sophomore, Junior, and Senior. And then I want to be able to get the Average GPA for those groups.

So a possible result set for these students would be:

Senior: 3.7
Junior: 2.9
Sophomore: 3.2
Freshman : 3.6


I'm not quite too sure how to go about getting this result though. I have tried things like
students.GroupBy(x => x.Level).Average();
but it does not work.

Any thoughts on this would be greatly appreciated. Thank you!

Answer Source

You have to use an additional Select, so you're looking for something like this:

var result = students.GroupBy(s => s.Level)
                     .Select(g => new {Level=g.Key, Avg=g.Average(s => s.GPA)});

Select(g => new {...}) creates an instance of a new anonymous type for each group.

That type has two properties:

  • Level, which is the Key property of the group
  • Avg, which is the average GPA of the group

Just "imagine" there's this type in use (more or less):

class GroupAverage
{
    public Level Level { get; set; }
    public float Avg { get; set; }
}

var result = students.GroupBy(s => s.Level)
                     .Select(g => new GroupAverage { Level=g.Key, Avg=g.Average(s => s.GPA) } );

but it simply has no name.


result is now:

enter image description here

Feel free to round the values if you need to.


To get the Level with the highest average, simply use

var highest = result.OrderByDescending(a => a.Avg).First().Level;

(Note that this will crash if there are no items in students)