No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Segundo Reto

31/35
Recursos

Este reto te servirá para mejorar tus consultas Linq.
Debes crear un reporte que muestre los mejores X promedios por asignatura, donde X es pasado como parámetro y regrese la asignatura y una lista de los Top X alumnos con su promedio.

Es recomendable darle una leída a Introducción a las consultas Linq y Operaciones básicas de consulta Linq.

Aportes 96

Preguntas 1

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Mi humilde solución:

        public Dictionary<string, IEnumerable<AlumnoPromedio>> GetListaTopPromedio(int x)
        {
            var resp = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicPromAlumPorAsignatura = GetPromeAlumPorAsignatura();

            foreach (var item in dicPromAlumPorAsignatura)
            {
                var dummy = (from ap in item.Value
                             orderby ap.promedio descending
                             select ap).Take(x);

                resp.Add(item.Key, dummy);
            }

            return resp;
        }

Los problemas que tuve que resolver buscando por internet fueron:

  1. Obtener primer elemento de la consulta, ya que por mas que el where devuelva un solo elemento sera en formato IEnumerable.
  2. Ordenarlo de manera descendente por promedio.
  3. Obtener la primera X cantidad de elementos.

Dejo mi solución:

public List<AlumnoPromedio> GetTopPromedioAlumnosEnAsignatura(string nombreAsignatura, int cantidad = 5) {
	var respuesta = new List<AlumnoPromedio>();
	var diccionarioPromediosPorAsignatura = GetPromedioAlumnoPorAsignatura();

	var alumnosFiltradosPorCantidad = 
		(from promedios in diccionarioPromediosPorAsignatura
			where promedios.Key.Equals(nombreAsignatura)
				select promedios.Value)
				.First() // Como siempre devuelve una lista, por mas que haya un elemento hay que tomar .First()
				.OrderByDescending(promedioAlumno => promedioAlumno.promedio)
				.Take(cantidad);
				
	respuesta.AddRange(alumnosFiltradosPorCantidad);

	return respuesta;
}

Saludos!

Otra opción podría ser retornando un IEnumerable de tipo object para darle mayor flexibilidad y seguridad en cuanto a tipos al listado de promedios:

public IEnumerable<object> GetMejoresPromxAsig(string asignatura, int cantidad = 5)
{
	var promAlumxAsig = GetPromAlumxAsig();
	var rta = promAlumxAsig.GetValueOrDefault(asignatura).OrderByDescending(prom => ((AlumnoPromedio) 
            prom).promedio).Take(cantidad);

	return rta;
}

Mi solucion del reto

Funcion para obtener un diccionario con el top de estudiantes.

Funcion para imprimir diccionarios.

Output

Mi solución 😄

 public Dictionary<string, IEnumerable<object>> TopPromedio(int top)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var dicEvalXAsig = GetDicEvaluaXAsig();

            foreach (var asigConEval in dicEvalXAsig)
            {
                var promsAlumn = (from eval in asigConEval.Value
                                  group eval by new
                                  {
                                      eval.Alumno.UniqueId,
                                      eval.Alumno.Nombre
                                  }
                            into grupoEvalsAlumno
                                  select new AlumnoPromedio
                                  {
                                      alumnoid = grupoEvalsAlumno.Key.UniqueId,
                                      alumnoNombre = grupoEvalsAlumno.Key.Nombre,
                                      //Por cada mimbro del volumen de datos estraer el valor nota (asigConEval.Value)
                                      promedio = grupoEvalsAlumno.Average(evaluacion => evaluacion.Nota)
                                  }).OrderByDescending(aluP => aluP.promedio).Take(top);

                rta.Add(asigConEval.Key, promsAlumn);
            }

            return rta;
        }```

Estuve pensando en hacer la misma consulta que hicimos con el método de obtener el promedio de alumnos por asignaturas pero estaría repitiendo código ya que la única diferencia que hay es que devolvemos el mismo diccionario de asignaturas con el top de promedios.

Por lo que mi solución ha sido llamar al método GetPromAluPorAsig, ordenarlo descendientemente por el promedio y coger el top que me pasan por parámetro, el código queda así:

public Dictionary<string, IEnumerable<PromedioAlumno>> GetTopAluPorAsig(int max) {
			var resp = new Dictionary<string, IEnumerable<PromedioAlumno>>();

			var dicPromAluPorAsig = GetPromAluPorAsig();
			foreach(var asig_aluProms in dicPromAluPorAsig) {
				var topProms = asig_aluProms.Value.Cast<PromedioAlumno>().OrderByDescending(e => e.promedio).Take(max);
				resp.Add(asig_aluProms.Key, topProms);
			}

			return resp;
		}

Tuve que cambiar todos los Object por tipo AlumnoPromedio para poder ordenar por la propiedad promedio

Muy aparte del reto es así como convertí todas las sentencias de LINQ de query syntax que es como nos enseño el profe Juan Carlos a Method Syntax.

 public IEnumerable<string> GetListSubject(out IEnumerable<Test> testList)
        {
            testList = GetListTest();

            return testList.Select(x => x.Subject.Name).Distinct();
        }

        public Dictionary<string, IEnumerable<Test>> GetTestsBySubject()
        {
            var dictionary = new Dictionary<string, IEnumerable<Test>>();
            var subjectsList = GetListSubject(out IEnumerable<Test> testsList);

            foreach (var subject in subjectsList)
            {
                var testListBySubject = testsList.Where(x => x.Subject.Name == subject);
                dictionary.Add(subject, testListBySubject);
            }
            return dictionary;
        }

        public Dictionary<string, IEnumerable<StudentAverange>> GetStudentAverageBySubject()
        {
            var studentAverageBySubjectDictionary = new Dictionary<string, IEnumerable<StudentAverange>>();
            var testDictionaryBySubject = GetTestsBySubject();

            foreach (var evalBySubject in testDictionaryBySubject)
            {

                var studentA = evalBySubject.Value.GroupBy(x => x.Student.Name);
                List<StudentAverange> studentAverange = new List<StudentAverange>();
                foreach (var studentScore in studentA)
                {
                    float average = studentScore.Average(x => x.Score);
                    string id = studentScore.First().Student.Id;
                    studentAverange.Add(new StudentAverange
                    {
                        StudentId = id,
                        StudentName = studentScore.Key,
                        Average = average
                    });
                }
                studentAverageBySubjectDictionary.Add(evalBySubject.Key, studentAverange);
            }
            return studentAverageBySubjectDictionary;
        }

        public Dictionary<string, IEnumerable<StudentAverange>> GetTopScoresBySubject(int numberOfTop = 1)
        {
            var topScoresList = new Dictionary<string, IEnumerable<StudentAverange>>();
            var testBySubjectAndStudent = GetStudentAverageBySubject();
            foreach (var evalBySubject in testBySubjectAndStudent)
            {
                var orderedList = evalBySubject.Value.OrderByDescending(x => x.Average)
                                                     .Take(numberOfTop);
                topScoresList.Add(evalBySubject.Key, orderedList);
            }
            return topScoresList;
        }

El método que he creado para devolver los x alumnos de una asignatura x, le he añadido otro método que despeja las tildes de los nombres de las asignaturas por si el usuario no introduce las tildes, ademas de igualar a minúsculas para la comparación.

public Dictionary<string, IEnumerable<object>> GetTopPromedioAsignatura(int num, string asignatura)
        {
            var datos = GetPromedioAlumnoAsignatura();
            var dicTop = new Dictionary<string, IEnumerable<object>>();


            foreach (var item in datos)
            {
                string Asign = EliminarTildes(item.Key);
                if(Asign.ToUpper() == asignatura.ToUpper())
                {
                var top = (from datatop in item.Value
                            orderby ((AlumnoPromedio)datatop).Promedio descending
                            select datatop).Take(num);
                
                dicTop.Add(item.Key, top);
                }
            }

            return dicTop;

        }```


public static string EliminarTildes(string palabra)
{
string normalizarstring = palabra.Normalize(NormalizationForm.FormD);
StringBuilder text = new StringBuilder();
for(int i = 0; i < normalizarstring.Length; i++)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(normalizarstring[i]);
if (uc != UnicodeCategory.NonSpacingMark)
{
text.Append(normalizarstring[i]);
}
}
return (text.ToString().Normalize(NormalizationForm.FormC));
}```

Bueno yo lo estuve intentando hacer de distintas formas pero la más pero que me pareció más simple es la siguiente

Reporteador.cs

   public Dictionary<string, IEnumerable<object>> GetPromeAlumnXAsig(
            // out Dictionary<string, IEnumerable<object>> ListGen,
            // out Dictionary<string, IEnumerable<object>> ListTop,
            int top = 10
        )
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var dicEvalXAsig = GetDicEvalXAsig();
            
            foreach (var asigConEval in dicEvalXAsig)
            {
                var promsAlum = from eval in asigConEval.Value
                            group eval by new{
                                eval.Alumno.UniqueId,
                                eval.Alumno.Nombre

                            } 
                            into grupoEvalsAlumno
                            select new AlumnoPromedio
                            {
                                alumnoId = grupoEvalsAlumno.Key.UniqueId, 
                                alumnoNombre = grupoEvalsAlumno.Key.Nombre, 
                                promedio = grupoEvalsAlumno.Average( evaluac => evaluac.Nota )
                            };
                rta.Add(asigConEval.Key, promsAlum.OrderByDescending((lp) => lp.promedio).Take(top));
            }
            return rta;
        }

Program.cs

	    listaPromXAsig = reporteador.GetPromeAlumnXAsig();
            Printer.WriteTitle("Lista De Alumnos Destacados");
            foreach (var asig in listaPromXAsig)
            {
                Printer.WriteTitle($"   {asig.Key}");
                var itm = 1;
                foreach (var Prom in asig.Value)
                {
                    switch (itm)
                    {
                        case 1:
                            WriteLine($"Primer Lugar {Prom} Felicitaciones!");
                            break;
                        case 2:
                            WriteLine($"Segundo Lugar {Prom} Felicitaciones!");
                            break;
                        case 3:
                            WriteLine($"Tercer Lugar {Prom} Felicitaciones!");
                            break;
                        default:
                            WriteLine(Prom);
                            break;
                    }
                    itm++;
                }
                
                
            }

AlumnoPromedio.cs

	public override string ToString()
        {
            return $"Alumno: \"{alumnoNombre}\", Promedio: {promedio}";
        }
public Dictionary\<string, IEnumerable\<AlumnoPromedio>> GetMejoresPromedio(int valoPromedio) { var rta = new Dictionary\<string, IEnumerable\<AlumnoPromedio>>(); var dicProAlumXAsig = GetPromedioAlumPorAsignatura(); foreach (var item in dicProAlumXAsig) { var ranking = from alProm in item.Value where alProm.promedio >= valoPromedio orderby alProm.promedio descending select alProm; rta.Add(item.Key, ranking); } return rta; }

Costó pero va quedando…

1: Crear un enum para las materias.

    public enum LlaveDiccionarioAsginaturas { 
    
        Matemática,
        Naturales,
        Sociales,
        Gimnasia
    }
  1. que el promedio por asignatura (completo) devuelva el dic con el objeto AlumnoPromedio como value
public Dictionary<string, IEnumerable<AlumnoPromedio>> GetPromedioAlumnosXAsignatura()
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicEvalXAsig = GetDicEvalPorAsig();
            foreach (var asignaturaConEvaluaciones in dicEvalXAsig)
            {
                var promediosAlumnos = from eval in asignaturaConEvaluaciones.Value
                                       group eval by new { 
                                           eval.Alumno.UniqueId,
                                           eval.Alumno.Nombre }
                                        into grupoEvalsAlumno
                                       select new AlumnoPromedio { 
                                           alId = grupoEvalsAlumno.Key.UniqueId, 
                                           promedio = grupoEvalsAlumno.Average(evaluacion => evaluacion.Nota), 
                                           alNombre = grupoEvalsAlumno.Key.Nombre };
                rta.Add(asignaturaConEvaluaciones.Key,promediosAlumnos);
            }

            return rta;
        }

  1. Que GetTopPromedioAlumnosXAsignatura devuelva una lista de obetos tipo AlumnoPromedio
        public List<AlumnoPromedio> GetTopPromedioAlumnosXAsignatura(LlaveDiccionarioAsginaturas asignatura, int topCantidad = 5)
        {
            var rta = new List<AlumnoPromedio>();
            var dicPromediosAlumnosXasignatura = GetPromedioAlumnosXAsignatura();
            var soloListaPromediosAsignatura = dicPromediosAlumnosXasignatura[asignatura.ToString()];
            var topAlumnos = soloListaPromediosAsignatura.OrderByDescending(alumno => alumno.promedio).Take(topCantidad);
            rta.AddRange(topAlumnos);
            return rta;
        }
public List<object> TopFive(string materia, int rango)
    {
        var promedios = GetPromeAlumnPorAsignatura();
        var top = promedios.GetValueOrDefault(materia).OrderByDescending(prom => ((AlumnoPromedio)prom).promedio
        ).Take(rango);

        return top.ToList();
    }

Un aporte de mi solución con impresión de resultados:

		public IEnumerable<object> GetMejorAlumnoPromedioPorAsignatura(string asignatura, int cantidad = 5)
		{

			var promAlumxAsig = GetPromedioAlumnPorAsignatura();
			var rta = promAlumxAsig.GetValueOrDefault(asignatura).OrderByDescending(prom => ((AlumnoPromedio)
					prom).promedio).Take(cantidad);

			Printer.WriteTitle($"En la materia {asignatura} el mejor promedio Top {cantidad} de estudiantes es:");
			foreach (var alumnos in rta)
			{
				var alumnoProm = (AlumnoPromedio)alumnos;

				Console.WriteLine($"{alumnoProm.alumnoNombre} con promedio {alumnoProm.promedio}");
			}
			return rta;
		}

public Dictionary<string, IEnumerable<object>> GetChallange(int max)
{
var result = new Dictionary<string, IEnumerable<object>>();
var dictResult = GetPromeAlumnPorAsignatura();

              foreach (var item in dictResult)
              {
                    var lista = item.Value.ToList().OrderByDescending(o => ((StudentCoverage)o).Coverage).ToList();
                    lista = lista.Take(max).ToList();
                    result.Add(item.Key, lista);
              }

              return result;
        }

Resultado-Solución

Notas >= 7.5

En mi humilde solución traté de cuidar como manejo los datos y cree nueva clase para manejar mis estructuras, no convenía seguir utilizando IEnumerable<object>, sabrá Dios que hay adentro jaja, se hizo una conversión a clase de aquello para tratar de mejor manera los datos.

File (Reporter.cs)

public List<BestAverageBySubject> GetBestAverageBySubjects(float bestAverageNum)
{
    var answer = new List<BestAverageBySubject>();

    var studentAverageIEnum = GetStudentAverageBySubject();

    foreach (var studentAverage in studentAverageIEnum)
    {
        List<Dictionary<string, float>> temporalListOfDict = new List<Dictionary<string, float>>();
        var temporalIEnum = convertObjectToIEnumerableStuAve(studentAverage.Value, bestAverageNum);

        foreach (var listStudent in temporalIEnum)
        {
            Dictionary<string, float> temporalDic = new Dictionary<string, float>(0);
            temporalDic.Add(listStudent.studentName, listStudent.average);
            temporalListOfDict.Add(temporalDic);
        }

        var obj = new BestAverageBySubject();
        obj.Subject = studentAverage.Key;
        obj.BestAverage = temporalListOfDict;
        answer.Add(obj);
    }
    return answer;
}

private IEnumerable<StudentAverage> convertObjectToIEnumerableStuAve(IEnumerable<object> obj, float bestAverageNum)
{
    var answer = from StudentAverage student in obj
                 where student.average > bestAverageNum
                 group student by new
                 {
                     student.average,
                     student.studentId,
                     student.studentName
                 }
                 into studentGroup
                 select new StudentAverage
                 {
                     average = studentGroup.Key.average,
                     studentId = studentGroup.Key.studentId,
                     studentName = studentGroup.Key.studentName
                 };
    return answer;
}

File (BestAverageBySubject.cs) in Entities

using System.Collections.Generic;

namespace CoreSchool.Entities
{
    public class BestAverageBySubject
    {
        public string Subject { get; set; }
        public List<Dictionary<string, float>> BestAverage { get; set; }
    }
}

File (Program.cs)

using CoreSchool.App;
using CoreSchool.Entities;
using CoreSchool.Util;
using static System.Console;
using System.Collections.Generic;
using System;

namespace CoreSchool
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.ProcessExit += EventAction;

            var engine = new SchoolEngine();
            engine.Initialize();
            Printer.WriteTitle("BIENVENIDOS A LA ESCUELA");
            WriteLine();

            var reporter = new Reporter(engine.GetDictionaryObjects());
            var stuTesList = reporter.GetStudentTestsList();
            var sbjList = reporter.GetSubjectsList();
            var studentTestDicBySubject = reporter.GetStudentTestDicBySubject();
            var studentAverageBySubject = reporter.GetStudentAverageBySubject();
            var bestAverageBySubjects = reporter.GetBestAverageBySubjects(7.5f);
            printBestAverages(bestAverageBySubjects);
        }

        private static void printBestAverages(List<BestAverageBySubject> bestAverageBySubjects)
        {
            Printer.WriteTitle("MEJORES PROMEDIOS");
            foreach (var subject in bestAverageBySubjects)
            {
                Printer.DrawLine(5);
                Printer.WriteTitle(subject.Subject);
                foreach (var dictStudent in subject.BestAverage)
                {
                    foreach (var student in dictStudent)
                    {
                        WriteLine($"Estudiante: {student.Key}, Nota: {student.Value}");
                    }
                }
            }
        }

        private static void EventAction(object sender, EventArgs e)
        {
            WriteLine();
            Printer.WriteTitle("FIN DEL PROGRAMA");
        }

        private static void PrintCoursesSchool(School school)
        {
            Printer.WriteTitle($"Cursos de \"{school.Name}\"");
            if (school?.Courses != null)
            {
                foreach (var course in school.Courses)
                {
                    WriteLine($"Nombre: \"{course.Name}\", Id: \"{course.UniqueId}\"");
                }
            }
        }
    }
}

Una opcion ,

utilice el mismo quey del ejercicio , solo adicione algo al final, y la variable top es el parámetro de entrada del método.

  var proAlumno = (from  eval in asieval.Value
                            group eval by  new {eval.Alumno.UniqueId , eval.Alumno.Nombre } 
                            into grupoEvalAlumno
                            select new AlumnoPromedio
                            { 
                              alumnokey = grupoEvalAlumno.Key.UniqueId,
                              alumnonombre = grupoEvalAlumno.Key.Nombre,
                              alumnoPromedio = grupoEvalAlumno.Average(evaluacion=>evaluacion.Nota)
                            }).OrderByDescending(r=> r.alumnoPromedio).Take(top);

Mi solución usando dos queries linq al final del comentario (.NET Core 3.1).

 public Dictionary<string, IEnumerable<object>> GetAsignaturaMejorPromedio(string asignatura, int cantidad)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var dicPromXAsig = GetPromeAlumnPorAsignatura();

            // traer las evaluaciones de  asignatura segun el parametro
            var promediossAlumnosAsignaturas = from eval in dicPromXAsig
                                where eval.Key == asignatura
                                select eval;

            // obtener por orden descendente 
                 var ListaMejorPromedio = (from AlumnoPromedio promedios in promediossAlumnosAsignaturas.First().Value
                                          orderby promedios.promedio descending
                                          select promedios).Take(cantidad).ToList();

            rta.Add(asignatura, ListaMejorPromedio);
            return rta;
        }

Mi solución:

public IEnumerable<Evaluacion> GetMejoresNotasPorAsignatura(string asignatura, int top = 5)
{
    var listaEvaluaciones = GetListaEvaluaciones();

    return (
        from Evaluacion ev in listaEvaluaciones
        where ev.Asignatura.Nombre.Equals(asignatura)
        select ev
    ).OrderByDescending(eval => eval.Nota).Take(top);
}

Y en el Program.cs utilicé el reporte así:

var listaAsignaturas = reporteador.GetListaAsignaturas();

foreach (var asignatura in listaAsignaturas)
{
    var mejoresNotas = reporteador.GetMejoresNotasPorAsignatura(asignatura, 3);
    Printer.WriteTitle($"{asignatura}");
    foreach (var evaluacion in mejoresNotas)
    {
        WriteLine($"Nota: {evaluacion.Nota}, Alumno: {evaluacion.Alumno.Nombre}");
    }
}

I receive: Reto de C#

.
.
.
.
.

I offer: Solucion



GetTopStudents

public Dictionary<string, IEnumerable<StudentAverage>> GetTopStudents(int studentAmount)
        {

            Dictionary<string, IEnumerable<StudentAverage>> averageGradeDic = GetAverageGrade();
            Dictionary<string, IEnumerable<StudentAverage>> studentsOrdered = new Dictionary<string, IEnumerable<StudentAverage>>();
            IEnumerable<StudentAverage> bestStudents;

            foreach (var aGrade in averageGradeDic)
            {

                bestStudents = from a in aGrade.Value
                               orderby a.average descending
                               select a;

                studentsOrdered.Add(aGrade.Key, bestStudents.Take(studentAmount));
            }

            return studentsOrdered;
        }

.
.
.
.
GetAverageGrade
Hay que notar que cambie el tipo de IEnumerable que este metodo retornaba, antes devolvia un objeto tipo object y ahora el objeto es tipo StudentAverage lo hice debido a que no queda claro a que se refiere lo que generaria confusion a algun futuro programador que toque mi codigo.

public Dictionary<string, IEnumerable<StudentAverage>> GetAverageGrade()
        {
            Dictionary<string, IEnumerable<StudentAverage>> answer = new Dictionary<string, IEnumerable<StudentAverage>>();

            Dictionary<string, IEnumerable<Evaluation>> dicEvalXcourseTopic = GetDicEvalByCourseTopic();

            foreach (var evalXct in dicEvalXcourseTopic)
            {

                var studentAverage = from eval in evalXct.Value
                                     group eval by new
                                     {
                                         eval.Student.Id,
                                         eval.Student.Name
                                     }
                                     into studentEvaluationsGroup
                                     select new StudentAverage
                                     {
                                         studentId = studentEvaluationsGroup.Key.Id,
                                         studentName = studentEvaluationsGroup.Key.Name,
                                         average = studentEvaluationsGroup.Average((ev) => ev.Grade),


                                     };

                answer.Add(evalXct.Key, studentAverage);

            }

            //return dictionary containing IEnumerables courseTopics, and inside them have StudentAverage.
            return answer;
        }

Aporte de: Facundo Castro

Mi solucion:

public Dictionary<string, IEnumerable<AlumnoPromedio>> Reto2(int TopX)
{
	 var lFiltrado = new Dictionary<string, IEnumerable<AlumnoPromedio> >();
	 var lDatos =  GetPromeAlumnPorAsignatura();

	 foreach (var item in lDatos)                          
	 {
		var FiltradoAsignatura = (
						from li in item.Value
						orderby li.promedio descending
						select li).Take(TopX);

		lFiltrado.Add(item.Key, FiltradoAsignatura);
	 }

	 return lFiltrado;
}
     	

MI solución propuesta sería:

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetPromedioAlumnosPorAsignatura(int top)
{
var result = GetPromedioAlumnosPorAsignatura();
foreach (var keyValuePair in result)
{
result[keyValuePair.Key] = keyValuePair.Value.OrderByDescending(x => x.Promedio).Take(top).ToList();
}
return result;
}

Adjunto mi solución

Les comparto mi solución, se que probablemente no es la mejor y quizás haya mejores maneras de hacerlo u optimizarlo, pero aún sigo aprendiendo.

Utilice la función Take() para obtener el número de evaluaciones que se quieren obtener por materia y el orderby (descending) para ordenar todos los alumnos por su promedio, aquí mi código:

public Dictionary<Asignatura, IEnumerable<AlumnoPromedio>> obtenerTopPromedioAlumnosPorAsignatura(int num)
	{
            var res = new Dictionary<Asignatura, IEnumerable<AlumnoPromedio>>();

            var diccionarioPromedios = obtenerPromedioAlumnosPorAsignatura();
		// Se recorre cada materia para ordenar y obtener los alumnos necesarios
            foreach (var itemDiccionario in diccionarioPromedios) 
            {
                var promedioAlumno = (from alumno in itemDiccionario.Value
                            orderby alumno.promedio descending //Ordena los elemento por el atributo promedio
                            select alumno).Take(num); // Con el take se toman solo los elementos indicados (parametro)
                res.Add(itemDiccionario.Key, promedioAlumno);                            
            }
            return res;
        }

Vengo a dejar mi resolución del reto, solo tuve la confusión sobre lo que dice el enunciado del problema y lo que dice el profesor al explicar el reto.

Porque entendí según el enunciado, que el método debía retornar el top dado por “X” (de todas las asignaturas), pero al explicar el reto parece que el profe pide que debe devolver el top pero de una única asignatura, en ese caso serían dos parámetros “X” y la “Asignatura” que se quiere devolver.

Por lo tanto decidí hacer dos métodos, uno para cada situación, dejo el código.


TOP “X” POR CADA ASIGNATURA

        public Dictionary<string, IEnumerable<Object>> GetTopPromedioPorCadaAsignatura(int top)
        {
            var respuesta = new Dictionary<string, IEnumerable<Object>>();
            var diccionarioEvaluacionesPorAsignatura = GetDiccionarioEvaluacionesPorAsignatura();
            foreach (var asignaturaConEvaluaciones in diccionarioEvaluacionesPorAsignatura)
            {
                var promediosAlumnos = (from evaluacion in asignaturaConEvaluaciones.Value
                                        group evaluacion by new
                                        {
                                            evaluacion.Alumno.UniqueId,
                                            evaluacion.Alumno.Nombre
                                        }
                            into grupoEvaluacionesAlumno
                                        select new PromedioAlumno
                                        {
                                            AlumnoId = grupoEvaluacionesAlumno.Key.UniqueId,
                                            AlumnoNombre = grupoEvaluacionesAlumno.Key.Nombre,
                                            Promedio = grupoEvaluacionesAlumno.Average(evaluacion => evaluacion.Nota)
                                        }).OrderByDescending(alumno => alumno.Promedio).Take(top);
                respuesta.Add(asignaturaConEvaluaciones.Key, promediosAlumnos);
            }
            return respuesta;
        }

\

TOP “X” DE UNA SOLA ASIGNATURA

        public Dictionary<String, IEnumerable<Object>> GetTopPromedioDeUnaAsignatura(string asignatura, int top)
        {
            var respuesta = new Dictionary<string, IEnumerable<Object>>();
            var topPromedios = GetTopPromedioPorCadaAsignatura(top);
            foreach (var asig in topPromedios)
            {
                if (asig.Key.Equals(asignatura))
                {
                    respuesta.Add(asignatura,asig.Value);
                }
            }
            return respuesta;
        }

Mi implementación haciendo uso de LinQ

  public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopAlumnosPorAsignatura(int top = 5)
        {
            var mejores = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicPromAlPorAsig = GetPromedioAlumnosPorAsignatura();

            foreach (var asignatura in dicPromAlPorAsig)
            {
                Printer.WriteTitle(asignatura.Key);

                var selection = (from alu in asignatura.Value
                                 orderby alu.promedio descending
                                 select alu).Take(top);

                mejores.Add(asignatura.Key, selection);
            }

            return mejores;
        }

este es mi solucion 😉
public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedioAlumnosAsignatura(int top)
{
var rtaTop = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
var dicPromedioAlumnoAsign = GetPromedioAlumnosAsignatura();
foreach (var promAsig in dicPromedioAlumnoAsign)
{
var lista = (promAsig.Value).OrderByDescending(al=>al.Promedio).Take(top).ToList();
rtaTop.Add(promAsig.Key,lista);
}
return rtaTop;
}

Por aqui mi solucion:

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetListaTopPromedio(int x = 10, string asig = "")
        {
            var asignaturasAlumnos = GetPromAlumnByAsignatura();

            var top = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

            foreach (var asignatura in asignaturasAlumnos)
            {

                if (!String.IsNullOrEmpty(asig))
                {
                    if (asig.ToLower() == asignatura.Key.ToLower())
                    {
                        var listTop = (from alum in asignatura.Value
                                       orderby alum.Promedio descending
                                       select alum).Take(x);
                        top.Add(asignatura.Key, listTop);
                        return top;
                    }
                }
                else
                {
                    var listTop = (from alum in asignatura.Value
                                   orderby alum.Promedio descending
                                   select alum).Take(x);
                    top.Add(asignatura.Key, listTop);
                }
            }

            return top;
        }

Hola, aquí anexo mi solución:

 public Dictionary<string, IEnumerable<AlumnoPromedio>> MejoresPromedios(int topAlumnos)
        {
            var promediosAlumnos = getPromedioAlumnoxAsign();

            var mejores = promediosAlumnos.Select(diccionario => new { 
                key = diccionario.Key, 
                Enumerable = diccionario.Value.OrderByDescending(prom => prom.promedio).Take(topAlumnos) 
            }).ToDictionary(x =>x.key,x =>x.Enumerable);

            return mejores;
        }

Hola, les dejo mi solución al reto

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetMejoresPromedioAlumnosPorAsignatura(int cantidadAlums)
        {
            var dictaRta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicAsigPromPorAlumno = this.GetPromedioAlumnosPorAsignature();

            foreach (var promAlumAsig in dicAsigPromPorAlumno)
            {
                var mejoresPromedios = (from alumProm in promAlumAsig.Value
                                       select alumProm).OrderByDescending(p => p.promedio).Take(cantidadAlums);

                dictaRta.Add(promAlumAsig.Key, mejoresPromedios);
            }
            return dictaRta;
        }

Una solución muy corta con un enfoque funcional:

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedioAlumnosXAsignatura(int cantidad = 5)
        {
            return GetPromedioAlumnosXAsignatura().ToDictionary(
                keyValuePair => keyValuePair.Key,
                keyValuePair => keyValuePair.Value.OrderByDescending(alumnoPromedio => alumnoPromedio.Promedio).Take(cantidad)
            );
        }```

Mi solucion, aportando lo aprendido en este curso

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetListaTopPromedioPorAsignatura(int cant = 10)
{
var rpta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
var dicPromNotaAlumPorAsignatura = GetPromedioAlumnoPorAsignatura();

        foreach (var promNotasAlumnXAsig in dicPromNotaAlumPorAsignatura)
        {
            var alumnoTopPromedio = (from pa in promNotasAlumnXAsig.Value
                         orderby pa.promedio descending
                         select pa).Take(cant);

            rpta.Add(promNotasAlumnXAsig.Key, alumnoTopPromedio);
        }

        return rpta;
    }

😎

Mi solución, espero que sirva a alguien.

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPremedioAlumnosXAsignatura(int cantidadAlumnos)
{
	var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
        var dicPromedioAlumnos = GetPromedioAlumnosXAsignatura();
        
	foreach (var asignatura in dicPromedioAlumnos)
         {
        	var topAlumn = (from alumn in asignatura.Value
                		orderby alumn.promedio descending
                                select alumn).Take(cantidadAlumnos);

		rta.Add(asignatura.Key,topAlumn);
	}
         return rta;
}

mi solucion uwu

  public Dictionary<LLaveAsignaturas, IEnumerable<AlumnoPromedio>> GetTopXPromedioAlumnXAsig(LLaveAsignaturas asignatura,  int X )
    {
      var rta = new Dictionary<LLaveAsignaturas, IEnumerable<AlumnoPromedio>>();
      var dicPromAlumnXAsig = GetPromedioAlumnXAsig();

      foreach (var asig in dicPromAlumnXAsig)
      {
        if (asig.Key == asignatura)
        {
                 var TopMejorPromedios = (from alumn in asig.Value
                                   orderby alumn.Promedio descending
                                   where asig.Key == (asignatura)
                                   select alumn).Take(X);
          rta.Add(asig.Key, TopMejorPromedios);
        }
      }
      
      return rta;
    }
        public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopAsignaturas(int x){
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

            var temp = GetPromedioAlumnPorAsignatura();
            foreach(var tmp in temp)
            {
                rta.Add(tmp.Key, tmp.Value.OrderByDescending(p=>p.Promedio).Take(x));
            }
            return rta;
        }

Les dejo mi solución, lo más dificil fue encontrar la funcionalidad de Take:

public IEnumerable<AlumnoPromedio> GetMejoresPromedios(int n, string asignatura)
        {
            
            var listEvalPorAsigPromedio = GetPromedioAlumPorAsignatura()[asignatura];
            if (listEvalPorAsigPromedio !=null)
            {
                var rta =   from prom in listEvalPorAsigPromedio
                            orderby prom.Promedio descending
                            select prom;
                return rta.Take(n);
            }
            else
            {
                Console.WriteLine("La Asignatura no existe");
                return null;
            }

            
        }
    public Dictionary<string, IEnumerable<AlumnoPromedio>> GetReporteMejoresPromedios(int top, string asignatura){

                var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
                var promedioPorAsignatura = GetPromedioAlumnosPorAsignatura();


                var result = promedioPorAsignatura
                .Where(asi => asi.Key.Equals(asignatura, StringComparison.OrdinalIgnoreCase))
                .Select(it => new 
                { 
                    Materia = it.Key ,
                    MejoresPromedios = it.Value.OrderByDescending(o => o.Promedio).Take(top) 
                });

            return rta;
        }
public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedioAlumnPorAsignatura(int top){
            var rta = GetPromedioAlumnPorAsignatura();
            var dataReturn = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            foreach (var item in rta)
            {
                var current =  (from data in item.Value
                orderby data.promedio descending select data).Take(top);
                dataReturn.Add(item.Key,current);
            }
            return dataReturn;
        }

Lo que se me ocurrió a mi es modificar el método que ya veníamos trabajando en la clase y así evitar generar código de más.

        public Dictionary<string, IEnumerable<AlumnoPromedioDTO>> GetPromedioAlumnosAsignatura (
                                                                int mejoresAlumnos = 0)
        {
            var respuesta = new Dictionary<string, IEnumerable<AlumnoPromedioDTO>>();
            var evaluacionesAsignatura = this.GetEvalucionesAsignatura();

            foreach (var asignaturaConEvaluacion in evaluacionesAsignatura)
            {
                var promediosAlumnos = from ev in asignaturaConEvaluacion.Value
                                           //orderby ev.Nota descending
                                       group ev by new { ev.Alumno.UniqueId, ev.Alumno.Name } // Clase anónima
                                       into groupAlumnoEvaluaciones
                                       select new AlumnoPromedioDTO()
                                       {
                                           Promedio = groupAlumnoEvaluaciones.Average(evaluacion => evaluacion.Nota),
                                           AlumnoId = groupAlumnoEvaluaciones.Key.UniqueId,
                                           AlumnoNombre = groupAlumnoEvaluaciones.Key.Name

                                       };
                if (mejoresAlumnos == 0)
                {
                    respuesta.Add(asignaturaConEvaluacion.Key, promediosAlumnos);

                }
                else
                {
                    respuesta.Add(asignaturaConEvaluacion.Key,
                                    promediosAlumnos
                                    .OrderByDescending(alumno => alumno.Promedio)
                                    .Take(mejoresAlumnos));
                }
            }
            return respuesta;
        }

Mi apor te

        public Dictionary<string, IEnumerable<AlumnoPromedio>> getTopPromedioAlumnos(string _asignatura, int _numberTop)
        {
            var _response = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var _asignaturaPorEvaluaciones = getDicccionarioAsignaturaPorEvaluaciones();

            var _topAlumnosPromedios = (from _topEvaluaciones in _asignaturaPorEvaluaciones.Where(x => x.Key == _asignatura).Select(y => y.Value).FirstOrDefault()
                                        orderby _topEvaluaciones.nota descending
                                        group _topEvaluaciones by new {
                                                _topEvaluaciones.alumno.uniqueId,
                                                _topEvaluaciones.nombre
                                        }
                                        into grupoTop
                                        select new AlumnoPromedio{
                                            alumnoId = grupoTop.Key.uniqueId,
                                            nombre = grupoTop.Key.nombre,
                                            promedio = grupoTop.Select(x => x.nota).FirstOrDefault()
                                        }).Take(_numberTop);

            _response.Add(_asignatura, _topAlumnosPromedios);


            return _response;
        }
    public Dictionary<string, IEnumerable<AlumnoPromedio>> getTopPromedioAlumnos(string _asignatura, int _numberTop)
    {
        var _response = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
        var _asignaturaPorEvaluaciones = getDicccionarioAsignaturaPorEvaluaciones();

        var _topAlumnosPromedios = (from _topEvaluaciones in _asignaturaPorEvaluaciones.Where(x => x.Key == _asignatura).Select(y => y.Value).FirstOrDefault()
                                    orderby _topEvaluaciones.nota descending
                                    group _topEvaluaciones by new {
                                            _topEvaluaciones.alumno.uniqueId,
                                            _topEvaluaciones.nombre
                                    }
                                    into grupoTop
                                    select new AlumnoPromedio{
                                        alumnoId = grupoTop.Key.uniqueId,
                                        nombre = grupoTop.Key.nombre,
                                        promedio = grupoTop.Select(x => x.nota).FirstOrDefault()
                                    }).Take(_numberTop);

        _response.Add(_asignatura, _topAlumnosPromedios);


        return _response;
    }
<public Dictionary<string, IEnumerable<AlumnoPromedio>> GetPromedioAlumnoXAsignatura(int cantidad)
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

            var dicPromXAsig = GetPromedioAlumnoXAsignatura();
                      

            foreach (var promAlu in dicPromXAsig)
            {
                var promediosTop = (from p in promAlu.Value
                                orderby p.promedio descending
                                select p).Take(cantidad);    

                rta.Add(promAlu.Key, promediosTop);
            }
            return rta;
        }> 

Hola compañeros quiero compartir este enlace, en el cual podemos encontrar un tutorial sobre LINQ

Mi solución incluye dos método sobrecargados el cual permite buscar el top para una materia o todas.

Use expresiones Lambda.

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedioAlumnoPorAsignatura(int numeroTop, string nombreAsignatura)
{
    var respuesta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

    IEnumerable<AlumnoPromedio> topPromedios =
        GetPromedioAlumnoPorAsignatura()
            .FirstOrDefault(item => item.Key == nombreAsignatura).Value
            .OrderByDescending(item => item.Promedio)
            .Take(numeroTop);

    respuesta.Add(nombreAsignatura, topPromedios);

    return respuesta;
}

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedioAlumnoPorAsignatura(int numeroTop)
{
    var respuesta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

    var promedioAlumnoPorAsignatura = GetPromedioAlumnoPorAsignatura();

    foreach (var asignatura in promedioAlumnoPorAsignatura)
    {
        respuesta.Add(asignatura.Key, asignatura.Value
            .OrderByDescending(item => item.Promedio)
            .Take(numeroTop));
    }

    return respuesta;
}

opte por hacerle unas mejoras al ultimo codigo

public Dictionary<string, IEnumerable<object>> GetTopPromAlumAsig(int cantidad)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var dicEvalXAsig = GetDicEvaluaXAsig();

            foreach (var asigConEval in dicEvalXAsig)
            {
                var promAlumnos = from eval in asigConEval.Value
                            group eval by new {
                                eval.Alumno.UniqueId,
                                eval.Alumno.Nombre}
                            into grupoEvalAlum
                            select new
                            {
                            alumnoId = grupoEvalAlum.Key.UniqueId,
                            alumnoNombre = grupoEvalAlum.Key.Nombre,
                            promedio = grupoEvalAlum.Average( evaluacion => evaluacion.nota)
                            };
                promAlumnos = promAlumnos.OrderByDescending((prom => prom.promedio)).ThenBy((nom => nom.alumnoNombre)).Take(cantidad);
                rta.Add(asigConEval.Key, promAlumnos);
            }

            return rta;
        }```

primer se ordena e forma descendente con orderby y luego se procede a tomar N cantidad de valores del diccionario con la funcion Take()

public Dictionary<string, IEnumerable<object>> GetTopNotas(int top)
        {
            var topNotas = new Dictionary<string, IEnumerable<object>>();
            
            var dicPapa = GetPromeAlumPorAsignatura();

            foreach(var asigConEval in dicPapa)
            {
                var topAlumn = from te in asigConEval.Value
                orderby te.promedio descending
                select new AlumnoPromedio
                {
                    alumnoId = te.alumnoId,
                    alumnoNombre = te.alumnoNombre,
                    promedio = te.promedio
                };

                topNotas.Add(asigConEval.Key, topAlumn.Take(top));
            }
            
            return topNotas;
        }

Mi reto:

public Dictionary<string, IEnumerable<object>> getTopAlumnoxAsignatura(int topX) {
    var rta = new Dictionary<string, IEnumerable<object>>();
    var dicEvalxAsig = getDicEvalxAsig();

    foreach (var asigConEval in dicEvalxAsig)
    {
        var promsAlumno = from eval in asigConEval.Value
                            group eval by new
                            {
                                eval.Alumno.UniqueId,
                                eval.Alumno.Nombre
                            }
                            into grupoEvalsAlumno
                            select new AlumnoPromedio {
                                alumnoId = grupoEvalsAlumno.Key.UniqueId,
                                alumnoNombre = grupoEvalsAlumno.Key.Nombre,
                                promedio = grupoEvalsAlumno.Average(vNota => vNota.Nota)
                            };
        rta.Add(asigConEval.Key, 
            promsAlumno.OrderByDescending(alum => alum.promedio).Take(topX));
    }
    return rta;
}

Cree las siguientes soluciones para el reto propuesto.
Realicé dos sobrecargas del mismo método con el fin de que el usuario pueda consultar el top x de todas las asignaturas a partir de una misma solicitud o el de una sola, que se específica por medio de un parámetro de tipo string.

Sobrecarga #1

Sobrecarga #2

No la verdad no puedo con el reto, me he perdido mucho con las explicaciones y la verdad no me queda claro muchas cosas, de repente se pierde el hilo

Hola, aquí les dejo mi solución:

El método para devolver los alumnos top de cada asignatura usa el método anterior que devuelve todos los promedios. El nuevo método toma el resultado anterior los ordena y toma los X mejores según el parámetro dado:

 public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedioAlumnosPorAsignatura(int top)
        {
            var diccionario = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var diccionarioPromedioAlumnosPorAsignatura = GetDiccionarioPromedioAlumnosPorAsignatura();

            foreach (var alumnoPromedio in diccionarioPromedioAlumnosPorAsignatura )
            {
                var alumnosTop = (from ap in alumnoPromedio.Value
                                    orderby ap.promedio descending
                                    select ap).Take(top);

                diccionario.Add(alumnoPromedio.Key, alumnosTop);
            }

            return  diccionario;
        }

Utilice orderby para ordenar la data de mayor a menor y luego utilice la función Take para sustraer las primeras X filas indicada por parámetros.

Aquí les dejo mi aporte usando Linq

 public Dictionary<string, IEnumerable<object>> GetTopMejoresProm(int x)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            // Traemos las lista de evaluaciones por asignatura
            var listaAsig = GetDictEvalXAsig();
            // Iteramos sobre la lista
            foreach (var asig in listaAsig)
            {
                // seleccionamos a alumno y lo agrupamos
                // Posteriormente ordenamos las notas descendentemente (asi obtendremos los promedios de mayor a menor)
                // Usamos el tipo AlumnoPromedio para seleccionar datos con el operador Distinct
                // Para seleccionar datos no repetidos
                // Finalmente con el operador Take pasamo el parametro 'x' que sera la cantidad de maximos promedios a mostrar
                var prom = ((from eval in asig.Value
                            group eval by new {
                                eval.Alumnno.UniqueId,
                                eval.Alumnno.Nombre
                            }
                            into topEval
                            orderby topEval.Average(evaluacion => evaluacion.Nota) descending
                            select new AlumnoPromedio
                            {
                                alumnoid = topEval.Key.UniqueId,
                                alumnoNombre = topEval.Key.Nombre,
                                promedio = topEval.Average(evaluacion => evaluacion.Nota)
                            }).Distinct()).Take(x);
                    rta.Add(asig.Key, prom);
            }
            return rta;
        }

Posteriormente en el Main method llamamos

var topProm = reporteador.GetTopMejoresProm(3);

Para el reto modifique el método de obtener el promedio de los alumnos por asignatura para reciba como parámetro opcional el nombre de la asignatura para que cada vez que se itere una asignatura y verifique que si se paso dicho parametro compare si la asignatura tiene dicho nombre, y obtenga el promedio, y de igual manera si la variable no se ha pasado se tome el promedio, de esta manera el método se optimiza para cuando se requiera.

        public Dictionary<string, IEnumerable<AlumnoPromedio>> GetPromeAlumnPorAsignatura(string nombreAsignatura = "")
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicEvalXAsig = GetDicEvaluaXAsig();

            foreach (var asigConEval in dicEvalXAsig)
            {
                if (string.IsNullOrEmpty(nombreAsignatura) || 
                    (!string.IsNullOrEmpty(nombreAsignatura) && asigConEval.Key == nombreAsignatura))
                {
                    var promsAlumn = from eval in asigConEval.Value
                        group eval by new 
                        { 
                            eval.Alumno.UniqueId,
                            eval.Alumno.Nombre
                        }
                        into grupoEvalsAlumno
                        select new AlumnoPromedio
                        {
                            alumnoId = grupoEvalsAlumno.Key.UniqueId,
                            alumnoNombre = grupoEvalsAlumno.Key.Nombre,
                            alumnoPromedio = grupoEvalsAlumno.Average(evaluacion  => evaluacion.Nota)
                        };
                
                    rta.Add(asigConEval.Key, promsAlumn);
                }
            }
            return rta;
        }

Y para obtener el top mande a llamar la lista anterior y la ordene de manera descendente para tener los promedios de mayor nota a menor nota y use la instruccion Take para tomar una cantidad X de promedios top.

        public Dictionary<string, IEnumerable<AlumnoPromedio>> GetAlumnosTopPorAsignatura(int cantidadTop, string nombreAsignatura)
        {
            Dictionary<string, IEnumerable<AlumnoPromedio>> listaTops = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

            var listaPromedios = GetPromeAlumnPorAsignatura(nombreAsignatura);

            foreach (var asignatura in listaPromedios)
            {
                var alumnosTop = (from promedios in asignatura.Value
                    orderby promedios.alumnoPromedio descending
                    select promedios).Take(cantidadTop);

                listaTops.Add(asignatura.Key, alumnosTop);
            }

            return listaTops;
        }
 public Dictionary<string,IEnumerable<AlumnoPromedio>> GetMejoresXProm(int x)
        {
            //Diccionario donde vamos a almacenar los resultados
            var dicc=new Dictionary<string,IEnumerable<AlumnoPromedio>>();
            //Diccionario donde obtenemos la asignatura con una lista de promedios de tipo AlumnoPromedio

            var dicctemp = GetPromeAlumnPorAsignatura();
          
            //Recorremos el diccionario que ya tiene los promedios calculados
            foreach (KeyValuePair<string,IEnumerable<AlumnoPromedio>> resultado in dicctemp)
            {
               //En esta lista guardamos todos los AlumnosPromedio de cada asignatura
                IEnumerable<AlumnoPromedio> listatemp = resultado.Value;
                //Con Lambda ordenamos los promedios de cada asignatura y tomamos los X
                listatemp.OrderByDescending(nota => nota.Promedio).Take(x);
                
                //Añadimos al diccionario
                dicc.Add(resultado.Key,listatemp);
            }


            return dicc;


        }```
public Dictionary<string, IEnumerable<object>> MejorPromedioPorAsig(int top)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();

            var evalAsig = GetDiccionarioDeEvaluacionesAsignatura();

            foreach (var asigConEval in evalAsig)
            {
                var topPromedio = (from eval in asigConEval.Value
                                   group eval by new { eval.Alumno.UniqueId, eval.Alumno.Nombre }
                                   into grupoEvalAlumno
                                   select new AlumnoPromedio {
                                       alumnoId = grupoEvalAlumno.Key.UniqueId,
                                       alumnoNombre = grupoEvalAlumno.Key.Nombre,
                                       promedio = grupoEvalAlumno.Average( eval => eval.Nota)
                                   })
                                   .OrderByDescending(alum => alum.promedio)
                                   .Take(top);

                rta.Add(asigConEval.Key, topPromedio);
            }

            return rta;
        }

Les comparto mi solución usando .OrderBy para ordenarlos de mayor a menor y después usando .Take para tomar los primeros n cantidad deseada de cada materia. Retorna un diccionario que después con bucles foreach se recorre y se imprimen los resultados.

public Dictionary<string, IEnumerable<float>> ExtractTopEvaluations(
            Dictionary<string, IEnumerable<StudentAverage>> studentAverages, int quantity)
        {
            //variable que contiene el diccionario de retorno
            var outTopEvaluations = new Dictionary<string, IEnumerable<float>>();
            //variable donde se guardan todos los promedios
            var data = new List<float>();
            
            foreach (var keyValPair in studentAverages)
            {
                //extrae los promedios de StudentAverage y los guarda en List data
                foreach (var obj in keyValPair.Value)
                {
                    data.Add(obj.average);
                }
                //guarda los promedios en orden descendente. después se toman los
                //primeros n cantidad(n mas altos)
                var outParameter = data.OrderByDescending(num => num).Take(quantity);
                //se añade la llave y el valor (lista de top promedios) al diccionario de salida
                outTopEvaluations.Add(keyValPair.Key, outParameter);
            }
            return outTopEvaluations;
        }```

Mi solucion

public Dictionary<string, IEnumerable<object>> GetMejorTopNoteXAsig(int top)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var listpromAsigxAlum = GetPromeTheStudent();


            foreach (var asigWithProm in listpromAsigxAlum)
            {

                var betterProm = (from better in asigWithProm.Value
                                  orderby better.promedio descending
                                  select better).Take(top);

                rta.Add(asigWithProm.Key, betterProm);





            }



            return rta;
        }

Mi solución al reto

        public Dictionary<string, IEnumerable<object>> GetAverageTopByMatter(int top=5)
        {
            var request = new Dictionary<string, IEnumerable<object>>();
            var dicEvalXAsig = GetDictionaryEvaluaXAsig();
            foreach (var asignatureConEval in dicEvalXAsig)
            {
                var averageStudent = (from eval in asignatureConEval.Value
                                     group eval by new
                                     {
                                         eval.Alumno.UniqueId,
                                         eval.Alumno.Nombre,
                                         eval.Asignatura

                                     }
                            into evalStudentGroup
                                     select new AlumnoPromedio
                                     {
                                         AlumnoId = evalStudentGroup.Key.UniqueId,
                                         AlumnoNombre = evalStudentGroup.Key.Nombre,
                                         Promedio = evalStudentGroup.Average(e => e.Nota)
                                     }).Take(top).OrderByDescending(x=>x.Promedio);
                request.Add(asignatureConEval.Key, averageStudent);
            }

            return request;
        }

Todavia me hago bolas con C#, creo que tengo que leer mas la documentacion.

**Para Platzi **: Si van a la sección de Archivos y Enlaces de este apartado, no se ve muy bien que tengan problemas de Unicode.

mi solucion

public Dictionary<string, IEnumerable<StudentAverage>> GetStudTopAvg(int qty)
        {
               var dictStudTopAvg = new Dictionary<string, IEnumerable<StudentAverage>>();
                  var studentAvgPerSubject = GetStudentAvgPerSubject();

                  foreach (var studKeyValAvg in studentAvgPerSubject)
                  {
                        
                              var studTopAvgList = (from stud in studKeyValAvg.Value
                                                   orderby stud.Average descending
                                                   
                                                   select new StudentAverage
                                                           {
                                                               Name = stud.Name,
                                                               Id = stud.Id,
                                                               Average = stud.Average
                                                           }).Take(qty);
                                                           
                      dictStudTopAvg.Add(studKeyValAvg.Key, studTopAvgList);
                     
                  }                    


               return dictStudTopAvg;
        }

public Dictionary<string, IEnumerable<StudentAverage>> GetStudTopAvg(int qty)
        {
               var dictStudTopAvg = new Dictionary<string, IEnumerable<StudentAverage>>();
                  var studentAvgPerSubject = GetStudentAvgPerSubject();

                  foreach (var studKeyValAvg in studentAvgPerSubject)
                  {
                         var studTopAvg = studKeyValAvg.Value.OrderByDescending(eva => eva.Average).Take(qty);
                              
                      dictStudTopAvg.Add(studKeyValAvg.Key, studTopAvg);
                  }                    


               return dictStudTopAvg;
        }

         
    }

Después de 2 días y plantearme si esto es lo mío, por fin lo terminé.

public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopEstudiantesXAsignatura (int cantidad)
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var listaPromedioXAsignatura = GetPromedioAlumnosXAsignatura();
            foreach (var listaPromedio in listaPromedioXAsignatura)
            {
                var lista = listaPromedio.Value.Cast<AlumnoPromedio>().OrderByDescending(eva => eva.promedio).Take(cantidad);
                rta.Add(listaPromedio.Key, lista);
            }

            return rta;
        }
 public Dictionary<string, IEnumerable<object>> GetTopPromedioXAsignatura(int top)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var dicEvalXAsig = GetListaEvaluaXasig();

            foreach (var asigConEval in dicEvalXAsig)
            {
                var promsAlumn = (from eval in asigConEval.Value
                            group eval by  new {
                                eval.Alumno.UniqueID,
                                eval.Alumno.Nombre
                            }
                            into grupoEvalsAlumno
                            orderby grupoEvalsAlumno.Average(evaluacion=> evaluacion.Nota) descending
                            select new
                            {
                                alumnoId = grupoEvalsAlumno.Key,
                                AlumnoNombre = grupoEvalsAlumno.Key.Nombre,
                                promedio = grupoEvalsAlumno.Average(evaluacion=> evaluacion.Nota)
                            }).Take(top);

                rta.Add(asigConEval.Key, promsAlumn);
            }
            return rta;
        }

Para la solución son muy importantes los métodos OrderByDescending() y Take()
Con la clases anteriores vimos como calcular los promedios, basta con primero ordenarlos de mayor a menor y luego tomar el numero que nosotros deseemos 😃

public Dictionary<string, IEnumerable<object>> GetMejoresAlum(int x=5)
        {
            var mejores = new Dictionary<string, IEnumerable<object>>();
            var dicEvalxasig = GetDicEvaluaXAsig();

            foreach (var asigEval in dicEvalxasig)
            {
                var mejorNota = (from mejor in asigEval.Value
                        group mejor by new{
                        mejor.Alumno.UniqueId, mejor.Alumno.Nombre
                }
                into grupoTop
                orderby grupoTop.Average(evaluacion => evaluacion.Nota) descending
                select new AlumnoPromedio
                {
                    alumnoNombre = grupoTop.Key.Nombre,
                    alumnoid = grupoTop.Key.UniqueId,
                    promedio = grupoTop.Average(evaluacion => evaluacion.Nota)
                }).Take(x);
                mejores.Add(asigEval.Key, mejorNota);
            }
            return mejores;
        }```
public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromedios(int cantidad = 1)
        {
            var rpta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var promedioAlumnosPorAsignatura = GetPromedioAlumnosPorAsignatura();
            foreach (var item in promedioAlumnosPorAsignatura)
            {
                var topPromedios = (from alumnoPromedio in item.Value
                                    orderby alumnoPromedio.Promedio descending
                                    select alumnoPromedio).Take(cantidad);
                rpta.Add(item.Key, topPromedios);
            }
            return rpta;
        }

hay mas metodos en LINQ para aplicar.

  public Dictionary<string, IEnumerable<object>> GetPromedioAlumnoXAsignatura() {
            var rta = GetTopPromedioAlumnoXAsignatura(0);
            return rta;
        }

        public Dictionary<string, IEnumerable<object>> GetTopPromedioAlumnoXAsignatura(int numtop)
        {
            var rta = new Dictionary<string, IEnumerable<object>>();
            var dicEvalXAsign = GetDicEvaluaXAsign();
            foreach (var asigConEval in dicEvalXAsign)
            {
                var promsAlumn = (from eval in asigConEval.Value
                                  group eval by new
                                  {
                                      eval.Alumno.UniqueId,
                                      eval.Alumno.Nombre
                                  } into grupoevalAlumn
                                  select new AlumnoPromedio
                                  {
                                      alumnoId = grupoevalAlumn.Key.UniqueId,
                                      promedio = grupoevalAlumn.Average(x => x.Nota),
                                      alumnoNombre = grupoevalAlumn.Key.Nombre
                                  })                              
                                 ;
                if (numtop > 0) 
                rta.Add(asigConEval.Key, promsAlumn.OrderByDescending(y => y.promedio).Take(numtop));
                else
                    rta.Add(asigConEval.Key, promsAlumn);
            }
            return rta;
        }
public Dictionary<string, IEnumerable<AlumnoPromedio> > GetPromedioAlumXAsg()
        {
            var dictionary = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

            var dicEvalXAsig =  GetEvaluxAsig();


            foreach (var item in dicEvalXAsig)
            {
                var promediosAlumnos = from ev in item.Value
                                        group ev by new
                                        { 
                                            ev.Alumno.UniqueId,
                                            ev.Alumno.Nombre
                                        }
                                        into  grupoEvalAlum
                                        select new AlumnoPromedio
                                        {
                                            AlumnoNombre = grupoEvalAlum.Key.Nombre,
                                            AlumnoId = grupoEvalAlum.Key.UniqueId,
                                            Promedio = grupoEvalAlum.Average( evalucion => evalucion.Nota )
                                        };
                dictionary.Add(item.Key, promediosAlumnos);
            }


            return dictionary;
        }

        public IEnumerable<AlumnoPromedio> GetTopPromedio(int tpo, string materia)
        {
            var list = new List<AlumnoPromedio>();

            var listPromedios = GetPromedioAlumXAsg();

            foreach (var item in listPromedios.Where(key => key.Key == materia))
            {
                list.AddRange( (from t in item.Value
                        where item.Key == materia
                        orderby t.Promedio descending
                        select t).Take(tpo).ToList()) ;  
                        Console.WriteLine("hey");
            }

            return list;
        }

public Dictionary<string, IEnumerable<float>> GetTopPromedio(int topPromedio){
            var list = new Dictionary<string, IEnumerable<float>>();
            var listaPromedios = GetPromedioAlumnoPorAsignatura();
            Console.WriteLine("Comienza FOREACH");
            foreach (var item in listaPromedios)
            {
               var prom = (from p in item.Value
                        orderby p.promedio descending
                        select p.promedio).Take(topPromedio);
                list.Add(item.Key, prom);
            }
            return list;
        }

Es mucho más simple de lo que se cree

    		rta.Add(asigConEval.Key, promsAlum.OrderByDescending((top) => top.promedio).Take(5));
            }
            return rta;
}

les dejo aca mi solucion del desafio:

public Dictionary<string, IEnumerable<object>> GetMejoresPromedioPorAsignatura(int top)
{
   	var rta = new Dictionary<string, IEnumerable<object>>();
	//recuperamos el diccionario con todos los promedios
        var dicPromedio = GetPromeAlumnPorAsignatura();

        //recorremos el diccionario
        foreach(var asig in dicPromedio)
        {
            //... con linq ordenamos los promedio de cada materia por valor de la nota
            // y tomamos los primeros x con la funcion Take vista en clases anteriores
            var topProm = (from eval in asig.Value
                           orderby eval.promedio descending
                           select eval).Take(top);

            //agregamos la meteria y la lista top a nuestro diccionario de resultados
            rta.Add(asig.Key, topProm);
        }

        return rta;
}```

Les dejo aca mi solución del desafío, saludos 😉

        ///<SUMMARY>
        /// Obtiene los mejores X promedios por asignatura. X es un valor de entrada a la funcion
        ///</SUMMARY>
        public Dictionary<string, IEnumerable<AlumnoPromedio>> showBestAverageXAsignatura(int cantidadMejoresAlumnos = 10)
        {
            var dic = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicPromXAsig = GetPromeAlumnPorAsignatura();

            foreach (var item in dicPromXAsig)
            {
                List<AlumnoPromedio> SortedList = item.Value.OrderByDescending(o => o.promedio).ToList();

                var bestAverageXAsign = (from promXasign in SortedList
                                         select promXasign).Take(cantidadMejoresAlumnos);
                dic.Add(item.Key, bestAverageXAsign);
            }
            return dic;
        }

Adjunto mi solución:

public Dictionary<string, IEnumerable<AlumnoPromedio>>
            GetTopAlumnosPorAsinatura(int top)
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicPromedioAlumnoXAsig = GetPromedioAlumnoXAsig();
            foreach (var asig in dicPromedioAlumnoXAsig)
            {
                var AlumnosTop = from alumn in
                                asig.Value.OrderByDescending(al => al.promedio).Take(top)
                                 select alumn;
                rta.Add(asig.Key, AlumnosTop);
            }
            return rta;
        }
<public Dictionary<string, IEnumerable<AlumnoPromedio>> GetBestPromsXAsig(int cantidad)
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>>();
            var dicProme = GetPromeAlumnPorAsignatura();

            foreach (var item in dicProme)
            {
                var dummy = item.Value.OrderByDescending(x=>x.promedio).Take(cantidad);

                rta.Add(item.Key, dummy);            
            }                                
            return rta;
        }>

Comparto mi solución

        public Dictionary<string, IEnumerable<AlumnoCualificado>> GetMejoresAlumnos(Dictionary<string, IEnumerable<AlumnoCualificado>> AsignaturaAndPromedio, int index = 5)
        {
            Dictionary<string, IEnumerable<AlumnoCualificado>> dicoAsigsPromedios;
            var dicoRta = new Dictionary<string, IEnumerable<AlumnoCualificado>>();

            if (AsignaturaAndPromedio != null)
            {
                dicoAsigsPromedios = AsignaturaAndPromedio;
            }
            else
            {
                Console.Write(nameof(dicoAsigsPromedios) + "is null");
                dicoAsigsPromedios = new Dictionary<string, IEnumerable<AlumnoCualificado>>();
            }

            foreach (var AsignaturaAndPromediosElement in dicoAsigsPromedios)
            {
                var MejoresAlumnosAsignatura = (from AlumnoCualificado alumno in AsignaturaAndPromediosElement.Value
                                                orderby alumno.Promedio descending
                                                select alumno).Take(index);

                dicoRta.Add(AsignaturaAndPromediosElement.Key, MejoresAlumnosAsignatura);

            }

            return dicoRta;
        }

Hola mi solución fue la siguiente:
Con el mismo código del método para obtener los promedios por asignatura, al momento de agregarlos en el diccionario solo ordené los promedios descendentemente con “OrderByDescending” y la expresión lambda. Y tomé solo la cantidad indicada con top que por default es 3

Para poder seleccionar la asignatura, ya en el método Main, al momento de llamar al método con el reporteador seleccioné la asignatura con “GetValueOrDefault” y mando como parámetro tipo string la asignatura que requiero.

Saludos.

Esta es mi solución:

Esta es mi solución

 public Dictionary<string, IEnumerable<AlumnoPromedio>> GetTopPromeAlumnPorAsignatura(int top, String tipoAsignatura)
        {
            var rta = new Dictionary<string, IEnumerable<AlumnoPromedio>> ();
            var resp=  GetPromeAlumnPorAsignatura().TryGetValue(tipoAsignatura, out var alumnosPromedio );
            IEnumerable<AlumnoPromedio> resultadotop=null;
            if (resp){
                resultadotop=( from alumno in alumnosPromedio.ToList()
                orderby alumno.promedio descending
                 select new AlumnoPromedio
                                 {
                                     alumnoid = alumno.alumnoid,
                                     alumnoNombre = alumno.alumnoNombre,
                                     promedio = alumno.promedio
                                 }).Take(top);
            } 
            
            rta.Add(tipoAsignatura,resultadotop) ; 
            return rta;
        }

Pienso que me falto el enum de la asignatura jajaaj

Hola. Traté de dar una solución un poco diferente a las que ya había en los comentarios. La hice con base al método GetPromeAlumnPorAsignatura el cual simplemente lo condicioné para que funcionara de dos formas. Espero que sea de ayuda para alguien.

public Dictionary<string, IEnumerable<Object>> GetPromeAlumnPorAsignatura(bool mejoresPromedio = false, int maximo = 5)
        {
            var rta = new Dictionary<string, IEnumerable<Object>>();
            var dicEvaluaXAsig = GetDicEvaluaXAsig();

            if (mejoresPromedio)
            {
                foreach (var asigConEval in dicEvaluaXAsig)
                {
                    var promsAlumn =    from eval in asigConEval.Value                                
                                        group eval by new {
                                            eval.Alumno.UniqueId,
                                            eval.Alumno.Nombre
                                        }
                                        into grupoEvalsAlumno
                                        orderby grupoEvalsAlumno.Average(evaluacion => evaluacion.Nota) descending                                 
                                        select new AlumnoPromedio{
                                            AlumnoId = grupoEvalsAlumno.Key.UniqueId,
                                            AlumnoNombre = grupoEvalsAlumno.Key.Nombre,
                                            Promedio = grupoEvalsAlumno.Average(evaluacion => evaluacion.Nota)
                                        };                                 

                    rta.Add(asigConEval.Key, promsAlumn.Take(maximo));
                }
            }else{
                foreach (var asigConEval in dicEvaluaXAsig)
                {
                    var promsAlumn = from eval in asigConEval.Value
                                group eval by new {
                                    eval.Alumno.UniqueId,
                                    eval.Alumno.Nombre
                                }
                                into grupoEvalsAlumno
                                select new AlumnoPromedio{
                                    AlumnoId = grupoEvalsAlumno.Key.UniqueId,
                                    AlumnoNombre = grupoEvalsAlumno.Key.Nombre,
                                    Promedio = grupoEvalsAlumno.Average(evaluacion => evaluacion.Nota)
                                }; 

                    rta.Add(asigConEval.Key, promsAlumn);
                }
            }
            
            return rta;
        }

MI solución al reto:

public Dictionary<string, List<PromedioAlumno>> GetListaTopPromedio(int x)
        {
            Dictionary<string, List<PromedioAlumno>> dicTopPromedios = new Dictionary<string, List<PromedioAlumno>>();
            Dictionary<string, List<PromedioAlumno>> dicPromedioPorAsignaturas = new Dictionary<string, List<PromedioAlumno>>();
            dicPromedioPorAsignaturas = GetDicPromedioPorAsignaturas();            

            foreach (var item in dicPromedioPorAsignaturas)
            {
                List<PromedioAlumno> lstPromedioAlumnos = new List<PromedioAlumno>();
                lstPromedioAlumnos.AddRange(item.Value.OrderByDescending(x => x.Promedio).Take(x));
                dicTopPromedios.Add(item.Key, lstPromedioAlumnos);
            }            

            return dicTopPromedios;
        }


var getRepTopPromedio = reporteador.GetListaTopPromedio(5);

            foreach (var item in getRepTopPromedio)
            {
                List<PromedioAlumno> lstPromedioAlumnos = item.Value.ToList();

                Console.WriteLine($"Materia: {item.Key}\n---TopPromedios---\n");

                foreach (var promAl in lstPromedioAlumnos)
                {
                    Console.WriteLine($"Nombre: {promAl.NombreAlumno} - Promedio: {promAl.Promedio} ");
                }

                Console.WriteLine("\n");
            }

        public Dictionary<string, IEnumerable<object>> GetListaTopPromedio(int x)
        {
            Dictionary<string, IEnumerable<object>> dicTopPromedios = new Dictionary<string, 				IEnumerable<object>>();
            var dicPromedioPorAsignaturas = GetDicPromedioPorAsignaturas();            

            foreach (var item in dicPromedioPorAsignaturas)
            {
                dicTopPromedios.Add(item.Key, (item.Value.OrderByDescending(x => x.Promedio)).Take(x));
            }            

            return dicTopPromedios;
        }```
    public Dictionary<string, IEnumerable<object>> GetTopXPromedios(int X){
        var rta = new Dictionary<string, IEnumerable<object>>();
        var listaAlumnoPromAsig = GetPromedioPorAlumnoAsignatura();

        foreach(var asig in listaAlumnoPromAsig){
            var listaTopX = (from prom in asig.Value
                             orderby ((AlumnoPromedio)prom).promedio descending
                             select prom).Take(X);
            rta.Add( asig.Key ,listaTopX);
        }
        
        return rta;
    }

Adjunto mi solución, hice una sobrecarga del método que ya nos devolvía los promedios por asignatura y con un poquito de investigación pude determinar que la función TAKE limita la cantidad de resultados devueltos por la consulta LINQ:

public Dictionary< string, IEnumerable<object> > GetPromAlumnoPorAsignatura( int top)
        {
            var respuesta = new Dictionary<string, IEnumerable<object>>();
            var diccEvalXAsig = GetDiccEvalxAsig();
            foreach (var asigConEval in diccEvalXAsig)
            {
                var promsAlumno = (from eval in asigConEval.Value
                            group eval by new
                            {
                                eval.Alumno.UniqueId,
                                eval.Alumno.Nombre
                            } 
                            into grupoEvalsAlumno
                            orderby grupoEvalsAlumno.Average( evalua => evalua.Nota ) descending
                            select new AlumnoPromedio
                            {
                                AlumnoId = grupoEvalsAlumno.Key.UniqueId,
                                Nombre = grupoEvalsAlumno.Key.Nombre,
                                Promedio = grupoEvalsAlumno.Average( evalua => evalua.Nota ),
                            }).Take(top);
                            //LINQ SIEMPRE DEVUELVE LISTAS
                respuesta.Add( asigConEval.Key, promsAlumno );
            }
            return respuesta;
        }
  public Dictionary<string, IEnumerable<AlumnoPromedio>> GetListaTopPromerdio(int top)
        {
            Dictionary<string, IEnumerable<AlumnoPromedio>> dicMejoresAlumnosPorAsignatura = new Dictionary<string, IEnumerable<AlumnoPromedio>>();

            var dicPromedioAlumnosPorAsignatura = GetPromedioAlumnosPorAsignatura();

            foreach (var asignatura in dicPromedioAlumnosPorAsignatura)
            {
                dicMejoresAlumnosPorAsignatura.Add(asignatura.Key, asignatura.Value.OrderByDescending(mm => mm.Promedio).Take(top));
            }

            return dicMejoresAlumnosPorAsignatura;
        }