'Average of List<objects> in c#

I have a list of object.

class Student
{
 int age;
 int height;
 int weight;
 int marksInMath;
 int marksInScience;
.
.
.
.
.
.
 int marksIn...;
}
List<Student> lst = new List<Student>();

I want to calculate median and average of this List. I am aware of

lst.Average(x=>x.Age);
lst.Average(x=>x.height);
.
.

Similarly for Median I can sort and then get median

lst.OrderBy(x=>x.Age);
//median logic on lst

But I don't want to repeat this code for every field(age, height, weight, marks, etc) in the object. Is there a way to do this in a loop or any other way so I don't have to get average for each field individually?



Solution 1:[1]

Here's the one pass way to compute averages:

var averages =
    lst
        .Aggregate(
            new
            {
                N = 0,
                Age = 0,
                Height = 0,
                Weight = 0,
                MarksInMath = 0,
                MarksInScience = 0
            },
            (a, x) =>
                new
                {
                    N = a.N + 1,
                    Age = a.Age + x.Age,
                    Height = a.Height + x.Height,
                    Weight = a.Weight + x.Weight,
                    MarksInMath = a.MarksInMath + x.MarksInMath,
                    MarksInScience = a.MarksInScience + x.MarksInScience,
                },
            a =>
                new
                {
                    Age = (double)a.Age / a.N,
                    Height = (double)a.Height / a.N,
                    Weight = (double)a.Weight / a.N,
                    MarksInMath = (double)a.MarksInMath / a.N,
                    MarksInScience = (double)a.MarksInScience / a.N,
                });

If you're after sums, stddev, etc, it's done the same way.

However, you're not going to be able compute the median without doing so on each property, one at a time.

Solution 2:[2]

I am at work so haven't been able to run this to see if it works. But if you can retrieve the values of each field using Student.fieldName then should be good. Not 100% on the studentStats.Add, whether that's how to add it or not. Just know I've done it before without needing the Tuple.

public List<(decimal avg, decimal med)> StudentScores(List<Student> students)
{
   var fieldNames = typeof(Student).GetFields().Select(field=>field.Name).ToList();
   var studentStats = new List<(decimal avg, decimal med)>();

   foreach(var field in fieldNames)
   {
       var average = 0;
       var count = 0;
       List<decimal> fieldMedian = new List<decimal>();
       foreach(var student in students)
       {
           count++
           totalScore = average + student.field;
           fieldMedian.Add(student.field);
       }
       average = totalScore / count;
       var sorted = fieldMedian.Sort();
       if(count%2 = 0)
       {
           var middle1 = count/2;
           var middle2 = (count/2)+1;
           var median = (sorted[middle1] + sorted[middle2]) / 2;
           studentStats.Add(average, median);
       }
       else
       {
           var middle = (count+1)/2;
           var median = sorted[middle];
           studentStats.Add(average, median);
       }
   }

   return studentStats; 
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Enigmativity
Solution 2 JaxValentine