Linq order by, group by and order by each group?

蹲街弑〆低调 提交于 2019-12-17 05:47:04

问题


I have an object that looks something like this:

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

I would like to create the following query: group grades by student name, order each student group by grades, and order groups by max grade in each group.

So it will look like this:

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

I created the following query:

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

But that returns IEnumerable IGrouping, and I have no way to sort the list inside, unless I do that in another foreach query and add the results to a different list using AddRange.

Is there a prettier way to do that?


回答1:


Sure:

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);

Note that you can get away with just taking the first grade within each group after ordering, because you already know the first entry will be have the highest grade.

Then you could display them with:

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



回答2:


The way to do it without projection:

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



回答3:


I think you want an additional projection that maps each group to a sorted-version of the group:

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

It also appears like you might want another flattening operation after that which will give you a sequence of students instead of a sequence of groups:

.SelectMany(group => group)

You can always collapse both into a single SelectMany call that does the projection and flattening together.


EDIT: As Jon Skeet points out, there are certain inefficiencies in the overall query; the information gained from sorting each group is not being used in the ordering of the groups themselves. By moving the sorting of each group to come before the ordering of the groups themselves, the Max query can be dodged into a simpler First query.




回答4:


try this...

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()));
    }
}



回答5:


Alternatively you can do like this :

     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))
        {
           ------------------------
           --------------------------
        }
     }


来源:https://stackoverflow.com/questions/5013710/linq-order-by-group-by-and-order-by-each-group

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!