2011-02-16 26 views
49

Tengo un objeto que se ve algo como esto:Linq ordenar, agrupar por orden y por cada grupo?

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

me gustaría crear la siguiente consulta: grados de grupo por el nombre del estudiante, ordenar cada grupo de estudiantes por grados, y los grupos de orden por grado máximo en cada grupo .

Por lo tanto, se verá así:

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

creé la siguiente consulta:

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

Pero eso devuelve IEnumerableIGrouping, y no tengo manera de ordenar la lista en el interior, a menos que yo hazlo en otra consulta foreach y agrega los resultados a una lista diferente usando AddRange.

¿Hay alguna forma más bonita de hacerlo?

Respuesta

101

Claro:

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

Tenga en cuenta que usted puede conseguir lejos con sólo tomar el primer grado dentro de cada grupo después de realizar el pedido, porque ya sabes la primera entrada se tiene el grado más alto.

Posteriormente, se podría mostrar con:

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

No hay ninguna propiedad clave en ese grupo variable var. –

+0

@David B: Whoops, corregido. –

+2

¡Gracias! Esta es la consulta finalizada - studentGrades.GroupBy (student => student.Name) .Select (group => new {Students = group.OrderByDescending (x => x.Grade)}) .OrderByDescending (group => group. Students.Primero(). Grado) .SelectMany (group => group.Students) – Rita

10

Creo que quieres una proyección adicional que se asigna a cada grupo a una versión ordenados del grupo:

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

También aparece como usted podría querer otra operación de aplanamiento después de eso que le dará una secuencia de estudiantes en lugar de una secuencia de grupos:

.SelectMany(group => group) 

siempre se puede colapsar tanto en una llamada única SelectMany que hace la proyección y el aplanamiento juntos.


EDIT: Como Jon Skeet señala, hay ciertas ineficiencias en la consulta general; la información obtenida al ordenar cada grupo no se utiliza en el orden de los grupos. Al mover la clasificación de cada grupo para que venga antes de ordenar los propios grupos, la consulta Max se puede eludir en una consulta más simple First.

+1

¡Muchas gracias! – Rita

+0

¡Fantástico, gracias! –

3

prueba esto ...

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

La manera de hacerlo sin proyección:

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

alternativa, se puede hacer como esto:

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