Linq упорядочивает, группирует и упорядочивает по каждой группе?
У меня есть объект, который выглядит примерно так:
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
.
Есть ли более красивый способ сделать это?
Ответы
Ответ 1
Конечно:
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);
}
}
Ответ 2
Я думаю, вам нужна дополнительная проекция, которая отображает каждую группу в отсортированную версию группы:
.Select(group => group.OrderByDescending(student => student.Grade))
Это также похоже на то, что после этого вам понадобится другая операция сглаживания, которая даст вам последовательность учеников вместо последовательности групп:
.SelectMany(group => group)
Вы всегда можете свернуть оба в один вызов SelectMany
, который выполняет проекцию и сплющивание.
EDIT:
Как указывает Джон Скит, в общем запросе есть определенная неэффективность; информация, полученная при сортировке каждой группы, не используется при упорядочении самих групп. Перемещая сортировку каждой группы до заказа самих групп, запрос Max
может быть уклонен в более простой запрос First
.
Ответ 3
Способ сделать это без проецирования:
StudentsGrades.OrderBy(student => student.Name).
ThenBy(student => student.Grade);
Ответ 4
попробуйте это...
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
В качестве альтернативы вы можете сделать следующее:
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))
{
------------------------
--------------------------
}
}