web-dev-qa-db-fra.com

Linq classer par, grouper par et classer par groupe?

J'ai un objet qui ressemble à quelque chose comme ça:

public class Student
{
    public string Name { get; set; } 
    public int Grade { get; set; }
}

J'aimerais créer la requête suivante: regrouper les notes par nom d'étudiant, classer chaque groupe d'étudiants par notes et classer les groupes par note maximale dans chaque groupe.

Donc ça va ressembler à ça:

A 100
A 80
B 80
B 50
B 40
C 70
C 30

J'ai créé la requête suivante:

StudentsGrades.GroupBy(student => student.Name)
    .OrderBy(studentGradesGroup => studentGradesGroup.Max(student => student.Grade));

Mais cela retourne IEnumerableIGrouping, et je n'ai aucun moyen de trier la liste à l'intérieur, à moins que je ne le fasse dans une autre requête foreach et ajoute les résultats à une autre liste à l'aide de AddRange.

Y a-t-il une façon plus jolie de faire cela?

60
Rita

Sûr:

var query = grades.GroupBy(student => student.Name)
                  .Select(group => 
                        new { Name = group.Key,
                              Students = group.OrderByDescending(x => x.Grade) })
                  .OrderBy(group => group.Students.First().Grade);

Notez que vous pouvez vous contenter de prendre la première note au sein de chaque groupe après la commande, car vous savez déjà que la première entrée aura la note la plus élevée.

Ensuite, vous pouvez les afficher avec:

foreach (var group in query)
{
    Console.WriteLine("Group: {0}", group.Name);
    foreach (var student in group.Students)
    {
        Console.WriteLine("  {0}", student.Grade);
    }
}
122
Jon Skeet

La façon de le faire sans projection:

StudentsGrades.OrderBy(student => student.Name).
ThenBy(student => student.Grade);
19
Sawyer

Je pense que vous voulez une projection supplémentaire qui mappe chaque groupe à une version triée du groupe:

.Select(group => group.OrderByDescending(student => student.Grade))

Il apparaît également comme vous peut-être voulez après l'opération une autre opération d'aplatissement qui vous donnera une séquence d'étudiants au lieu d'une séquence de groupes:

.SelectMany(group => group)

Vous pouvez toujours réduire les deux en un appel niqueSelectMany qui effectue la projection et l'aplatissement ensemble.


EDIT: Comme le souligne Jon Skeet, il y a certaines inefficacités dans la requête globale; les informations obtenues en triant chaque groupe ne sont pas utilisées dans l'ordre des groupes eux-mêmes. En déplaçant le tri de chaque groupe à venir avant l'ordre des groupes eux-mêmes, la requête Max peut être modifiée dans une requête plus simple First.

15
Ani

essaye ça...

public class Student 
    {
        public int Grade { get; set; }
        public string Name { get; set; }
        public override string ToString()
        {
            return string.Format("Name{0} : Grade{1}", Name, Grade);
        }
    }

class Program
{
    static void Main(string[] args)
    {

      List<Student> listStudents = new List<Student>();
      listStudents.Add(new Student() { Grade = 10, Name = "Pedro" });
      listStudents.Add(new Student() { Grade = 10, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 10, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 11, Name = "Mario" });
      listStudents.Add(new Student() { Grade = 15, Name = "Mario" });
      listStudents.Add(new Student() { Grade = 10, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 10, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 11, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 22, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 55, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 77, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 66, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 88, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 42, Name = "Pedro" });
      listStudents.Add(new Student() { Grade = 33, Name = "Bruno" });
      listStudents.Add(new Student() { Grade = 33, Name = "Luciana" });
      listStudents.Add(new Student() { Grade = 17, Name = "Maria" });
      listStudents.Add(new Student() { Grade = 25, Name = "Luana" });
      listStudents.Add(new Student() { Grade = 25, Name = "Pedro" });

      listStudents.GroupBy(g => g.Name).OrderBy(g => g.Key).SelectMany(g => g.OrderByDescending(x => x.Grade)).ToList().ForEach(x => Console.WriteLine(x.ToString()));
    }
}
4
Bruno Torres

Sinon, vous pouvez faire comme ça:

     var _items = from a in StudentsGrades
                  group a by a.Name;

     foreach (var _itemGroup in _items)
     {
        foreach (var _item in _itemGroup.OrderBy(a=>a.grade))
        {
           ------------------------
           --------------------------
        }
     }
1
agileDev