-1

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?

binaryCoder
  • 45
  • 1
  • 5
  • 3
    You could get the list of properties of a class an iterate through them. [This thread](https://stackoverflow.com/questions/737151/how-to-get-the-list-of-properties-of-a-class) illustrates how. Would this help? – cSharp Apr 18 '22 at 23:30
  • 1
    There's no other way, you have to process each property individually. Now that could all be in a single loop or in separate Linq calls,, and you can name each property explicitly or loop over them using reflection, but you can only access one property at a time – Pranav Hosangadi Apr 18 '22 at 23:32
  • I am aware that I have to access each property to calculate the average, I am hoping to get it done in a single loop maybe. – binaryCoder Apr 18 '22 at 23:44
  • Perhaps creating a custom data-structure, that you could use to add "new students", the DS class could contain properties like "AverageAge, MedianAge, AverageHeight, MedianHeight"? Is it necessary to be in a loop? – Rickless Apr 18 '22 at 23:46
  • 2
    Please note that you dont have any properties. none. what you have are *fields*. and not even public ones at that – Ňɏssa Pøngjǣrdenlarp Apr 18 '22 at 23:49
  • @noobCoder - When asking a question, please provide full C# code for your classes, and also provide sample data (again in C# code) that we can use to demonstrate an answer. – Enigmativity Apr 19 '22 at 01:12
  • Note that `.OrderBy` doesn't change the order of the list. It just returns an `IEnumerable` that, when iterated, is in the ascending order of Age. But you're not using that. – ProgrammingLlama Apr 19 '22 at 01:15
  • You can certainly loop through all the students and manually calculate the mean for each field as you go. As the median is the middle value of an ordered set, you won't be able to calculate this in a single loop as the set defined by the values for each field will have a different order. – ProgrammingLlama Apr 19 '22 at 01:30

2 Answers2

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
-1

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; 
}
  • If someone is able to provide some clarification of why this was voted down so I can learn to give better answers, would be appreciated. – JaxValentine Apr 22 '22 at 00:28