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)