Linq按顺序排序,按组分组并按每个组排序?

86

我有一个对象,看起来像这样:

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

我想创建以下查询:按学生姓名分组成绩,按成绩对每个学生组进行排序,并按每个组中的最高成绩对组进行排序。

因此它将如下所示:

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

我创建了以下查询:

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

但是这会返回 IEnumerable IGrouping,我无法在其中对列表进行排序,除非我在另一个 foreach 查询中执行此操作,并使用 AddRange 将结果添加到另一个列表中。

有没有更好的方式?

5个回答

163
当然:
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);

请注意,在排序后,您只需获取每个组中的第一等级,因为您已经知道第一个条目将具有最高等级。
然后,您可以使用以下方式显示它们:
foreach (var group in query)
{
    Console.WriteLine("Group: {0}", group.Name);
    foreach (var student in group.Students)
    {
        Console.WriteLine("  {0}", student.Grade);
    }
}

3
该 var group 变量上没有 Key 属性。 - Amy B
6
谢谢!这是已完成的查询 - studentGrades.GroupBy(student => student.Name) .Select(group => new { Students = group.OrderByDescending(x => x.Grade) }) .OrderByDescending(group => group.Students.First().Grade) .SelectMany(group => group.Students) - Rita

22

无需投影的实现方法:

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

3
我认为原因在于,虽然该数据将按照要求进行排序,但这只是偶然的。最优秀学生的顺序也是按字母顺序排列的。如果你给C学生一个90分的成绩,他们应该出现在B学生之上。但在这个答案中,他们不会出现在B学生之上。 - Red

18

我认为您想要一个附加的投影,将每个组映射到该组的排序版本:

.Select(group => group.OrderByDescending(student => student.Grade))
似乎您还需要进行另一次展平操作,以获取学生序列而不是组序列。
.SelectMany(group => group)

你可以将它们合并为一个 单独的 SelectMany 调用,此调用可以同时进行投影和扁平化。


编辑: 正如Jon Skeet所指出的那样,整个查询存在某些低效率; 每个组的排序信息没有用于对组本身的排序。通过将每个组的排序移动到组本身排序之前,Max 查询可以转换为更简单的 First 查询。


5

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

2

或者您可以这样做:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接