Home > Enterprise >  how to calculate average for each item in a list with LINQ?
how to calculate average for each item in a list with LINQ?

Time:11-03

How can I calculate each of the averages of the students? I did this ... but the average does not work for me, how could I do it? With a JoinGroup and then a GroupBy ?, I wait to see solutions, thanks.

var listadoA = alumnos.Join(examenes, 
                        a => a._id,
                        e => e._alumnoId,
                        (a, e) => new
                        {
                            NombreAlumno = a._nombre,
                            Examenes = examenes,
                            Notas = e._nota,
                        }).Where(p => p.Examenes.Count() >= 1).OrderBy(p => p.NombreAlumno).ToList();
        foreach (var obj in listadoA){
            var promedio = obj.Average(p => p.Nota);
            Console.Write($"\nAlumno = {obj.NombreAlumno}, Promedio ={promedio}");
        }   


class Examen{
    public double _nota{get;set;}
    public int _alumnoId {get;set;}
    public int cursoId{get;set;}


    public Examen(int id, double nota, int idMateria){
        this._alumnoId = id;
        this.cursoId = idMateria;
        this._nota = nota;
    }

    public override string ToString(){
        return ($"Alumno = {this._alumnoId}, Nota = {this._nota}, Curso = {this.cursoId}");
    }
    public static List<Examen> GetLista(){
        return new List<Examen>(){
            new Examen(2,5,1),
            new Examen(4,7,5),
            new Examen(4,9,3),
            new Examen(3,10,4),
            new Examen(7,5,3),
            new Examen(2,8,4),
            new Examen(6,9,5),
            new Examen(9,7,1),
            new Examen(6,5,4),
            new Examen(9,1,4),
            new Examen(7,9,5),
        };
    }
}

CodePudding user response:

I'm a bit short on time to test it but I think it should work with a few small tweaks. If I've made any typos, let me know:

var listadoA = alumnos.GroupJoin(examenes, 
                    a => a._id,
                    e => e._alumnoId,
                    (a, eGroup) => new
                    {
                        Alumno = a,
                        Examenes = eGroup
                        
                    }).Where(p => p.Examenes.Count() >= 1).OrderBy(p => p.Alumno._nombre).ToList();
foreach (var obj in listadoA){
        var promedio = obj.Examenes.Average(e => e._nota);

I'm curious why your fields starting with underscore are publicly accessible; that's the naming convention for a private field.. should really have public properties for them. Also, I've assumed that "nota" is the exam score..

CodePudding user response:

EDIT

The answer was originaly posted before precisions were made about the classes involved. I still keep the english naming, because it might be clearer for a wider audience. This also helps clarify my answer in regards to @Caius Jard pertinent comment.

Using Linq to object: (no use of Entity Framework)

This code does the following tradeof compared to the use of join. It might be less performant, but it is more simple (you don't even have to understand what a join is).

AveragesByStudents = Students
    .Select(s => new 
    {
        StudentName = s.Name,
        Notes = Exams
            .Where(e => e.StudentId == s.Id)
            .Select(e => e.Note)
            .ToList()
    })
    .Select(s => new 
    {
        s.StudentName,
        Average = s.Notes.Any() ? s.Notes.Average() : null
    });
;

With this example, you obtain all the students, even if they have no notes (in that case their average is null). You could do it in one select, but it would not be more readable.

With the following example, you obtain only the students that have notes, so their average cannot be null.

AveragesByStudents = Students
    .Select(s => new 
    {
        StudentName = s.Name,
        Notes = Exams
            .Where(e => e.StudentId == s.Id)
            .Select(e => e.Note)
    })
    .Where(s => s.Notes.Any())
    .Select(s => new 
    {
        s.StudentName,
        Average = Notes.Average()
    });
;

Add a ToList() at the end of queries if you want to materialize.

  • Related