Linq упорядочить, группировать и упорядочить по каждой группе? - PullRequest
58 голосов
/ 16 февраля 2011

У меня есть объект, который выглядит примерно так:

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 ]

120 голосов
/ 16 февраля 2011

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

Обратите внимание, что вы можете просто получить первый класс в каждой группе после заказа, потому что вы уже знаете, что первая запись будет иметь самый высокий балл.

Тогда вы можете отобразить их с помощью:

foreach (var group in query)
{
    Console.WriteLine("Group: {0}", group.Name);
    foreach (var student in group.Students)
    {
        Console.WriteLine("  {0}", student.Grade);
    }
}
18 голосов
/ 08 июля 2013

Способ сделать это без проекции:

StudentsGrades.OrderBy(student => student.Name).
ThenBy(student => student.Grade);
15 голосов
/ 16 февраля 2011

Я думаю, вам нужен дополнительный прогноз, который отображает каждую группу в отсортированную версию группы:

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

Также кажется, что вы могли бы захотеть еще одну операцию выравнивания после того, что даст вам последовательность учеников вместо последовательности групп:

.SelectMany(group => group)

Вы всегда можете свернуть оба в одиночный SelectMany вызов, который выполняет проекцию и выравнивание.


EDIT: Как отмечает Джон Скит, в общем запросе есть определенные недостатки; информация, полученная в результате сортировки каждой группы, не используется для упорядочения самих групп. Перемещая сортировку каждой группы по порядку перед упорядочением самих групп, запрос Max можно разбить на более простой запрос First.

4 голосов
/ 26 ноября 2011

попробуйте это ...

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()));
    }
}
1 голос
/ 21 мая 2015

В качестве альтернативы вы можете сделать так:

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