Here’s an explanation of how the default example for LINQ nested grouping actually works. The usual example for nested grouping looks like this:
from student in Students group student by student.Faculty into Faculty from dbtgroup in ( from student in Faculty group student by student.DebtCategory ) group dbtgroup by Faculty.Key;
The objective of this statement is to first group-by students into faculties and then in each faculty create subgroupings of students by their DebtCategory.
So how does this actually work and whats the equivalent method/lamba syntax? The first step is to groups each student into their faculty. Assume we have the following data
public class Student { public string Name { get; set; } public string Faculty { get; set; } public int DebtCategory { get; set; } } IList<Student> Students = new List<Student>(); Students.Add(new Student { Name = "John" , Faculty = "IT" , DebtCategory = 2 }); Students.Add(new Student { Name = "Jane" , Faculty = "IT" , DebtCategory = 2 }); Students.Add(new Student { Name = "Jesse", Faculty = "Finance", DebtCategory = 2 }); Students.Add(new Student { Name = "Linda", Faculty = "Finance", DebtCategory = 1 });
The following query groups each student into a faculty
var query1 = from student in Students group student by student.Faculty into Faculty select Faculty; //The Method syntax for the above query is: var query1Method = Students .GroupBy(student => student.Faculty) .Select ( Faculty => Faculty); //This gives us the following IGrouping<string, Student> as result // // [0] // Key : IT // Values: // [0] John (IT) (2) // [1] Jane (IT) (2) // // [1] // Key : Finance // Values: // [0] Jesse (Finance) (2) // [1] Linda (Finance) (1)
The next step is to add another level of grouping:
var query2 = from student in Students group student by student.Faculty into Faculty from dbtgroup in ( from student in Faculty group student by student.DebtCategory ) select dbtgroup; //This gives us the following IGrouping<int, Student> as result //[0] // Key : 2 // Values: // [0] John (IT) (2) // [1] Jane (IT) (2) // //[1] // Key : 2 // Values: // [0] Jesse (Finance) (2) // //[2] // Key : 1 // Values: // [0] Linda (Finance) (1) // The following is the literal translation of the above Comprehension syntax into method syntax. We're ignoring this as explained below // var query2Method = Students // .GroupBy(student => student.Faculty) // .SelectMany( Faculty =>Faculty.GroupBy(student => student.DebtCategory) // , (Faculty, dbtgroup) => dbtgroup); //The final complete query ends with"group dbtgroup by Faculty.Key;" // this statement causes the compiler to see that you're refering to the Faculty object from the select many, so instead of // "(Faculty, dbtgroup) => dbtgroup" it emits a slightly different projection "(Faculty, dbtgroup) => new {Faculty, dbtgroup} //structure var query2Method = Students .GroupBy(student => student.Faculty) .SelectMany( Faculty =>Faculty.GroupBy(student => student.DebtCategory) , (Faculty, dbtgroup) => new {Faculty, dbtgroup});
Query2 is close to our desired output, however the grouping is the wrong way around. So the final step is:
var query3 = from student in Students group student by student.Faculty into Faculty from dbtgroup in ( from student in Faculty group student by student.DebtCategory ) group dbtgroup by Faculty.Key; //The method/lambda syntax is: var query3Method = Students .GroupBy(student => student.Faculty) .SelectMany ( Faculties => Faculties.GroupBy (student => student.DebtCategory) , (Faculty, dbtgroup) => new { Faculty = Faculty, dbtgroup = dbtgroup } ) .GroupBy( item => item.Faculty.Key, item => item.dbtgroup ); //This gives us the following groups as result //[0] // Key : IT // Values: // [0] Key : 2 // Values: // [0] John (IT) (2) // [1] Jane (IT) (2) //[1] // Key : Finance // Values: // [0] Key : 2 // Values: // [0] Jesse (Finance) (2) // [1] Key : 1 // Values: // [0] Linda (Finance) (1)