I am trying to write a nested GroupBy LINQ expression using the fluent (i.e., "method") syntax.
Here's my class and data:
class Person
{
public String ZipCode, Gender, Name;
}
private static List<Person> people = new List<Person>
{
new Person { ZipCode= "11111", Gender = "M", Name = "Tom" },
new Person { ZipCode= "11111", Gender = "M", Name = "Bob" },
new Person { ZipCode= "11111", Gender = "F", Name = "Nancy" },
new Person { ZipCode= "11111", Gender = "F", Name = "Lisa" },
new Person { ZipCode= "22222", Gender = "M", Name = "Dan" },
new Person { ZipCode= "33333", Gender = "F", Name = "Mary" },
new Person { ZipCode= "44444", Gender = "F", Name = "Joan" },
new Person { ZipCode= "44444", Gender = "F", Name = "Jane" },
new Person { ZipCode= "44444", Gender = "M", Name = "Bill" }
};
What I'm wanting to achieve is an object that is grouped by ZipCode
and then by Gender
within ZipCode
. In terms of an object type, I am looking for this:
IEnumerable<IGrouping<string, IEnumerable<IGrouping<string, Person>>>>
This would then allow me to access the query results like this:
foreach(var byZip in mainQuery) {
Console.WriteLine(byZip.Key); // print the ZipCode
foreach(var byGender in byZip) {
Console.WriteLine(byGender.Key) // print the Gender
foreach (Person p in byGender)
Console.WriteLine(p.Name);
}
}
Again, I'm looking to use fluent notation.
Based on Jeff's answer below, this is the query and the access:
IEnumerable<IGrouping<string, IEnumerable<IGrouping<string, Person>>>> query =
people
.GroupBy(p => p.ZipCode)
.GroupBy(
keySelector: g => g.Key,
elementSelector: g => g.GroupBy(p => p.Gender)
);
foreach (IGrouping<string, IEnumerable<IGrouping<string, Person>>> byZip in query)
{
Console.WriteLine(byZip.Key); // print the ZipCode
foreach (IEnumerable<IGrouping<string, Person>> byGender in byZip)
{
foreach (IGrouping<string, Person> t in byGender)
{
Console.WriteLine(t.Key); // print the Gender
foreach (Person y in t)
Console.WriteLine(y.Name);
}
}
}
In query syntax, you could write it like this:
var query =
from p in people
group p by p.ZipCode into g
let gender =
from p in g
group p by p.Gender
group gender by g.Key;
And converting it using the "fluent" syntax, that would become this:
var query2 = people.GroupBy(p => p.ZipCode)
.GroupBy(
g => g.Key,
g => g.GroupBy(p => p.Gender)
);
However the way you want to access this doesn't match the actual type. Your access pattern makes your type actually be:
IEnumerable<IGrouping<string, IGrouping<string, Person>>>
So the query should be:
var query =
from p in people
group p by p.ZipCode into g
let gender =
from p in g
group p by p.Gender
from g2 in gender
group g2 by g.Key;
And the conversion... is a bit more complicated.
var query2 = people.GroupBy(p => p.ZipCode)
.SelectMany(g =>
g.GroupBy(p => p.Gender).GroupBy(g2 => g.Key)
);
The following:
var peopleGroupedByZipCode = people
.GroupBy(p => p.ZipCode)
.Select(group => new
{
ZipCode = group.Key,
PeopleGroupedByGender = group.GroupBy(p => p.Gender)
});
foreach (var resultsByZipCode in peopleGroupedByZipCode)
{
Console.WriteLine("ZipCode: " + resultsByZipCode.ZipCode);
foreach (var resultsByGender in resultsByZipCode.PeopleGroupedByGender)
{
Console.WriteLine(" Gender : " + resultsByGender.Key);
foreach (var resultWithinGenderGroup in resultsByGender)
Console.WriteLine(" " + resultWithinGenderGroup.Name);
}
}
will write out the following:
ZipCode: 11111
Gender : M
Tom
Bob
Gender : F
Nancy
Lisa
ZipCode: 22222
Gender : M
Dan
ZipCode: 33333
Gender : F
Mary
ZipCode: 44444
Gender : F
Joan
Jane
Gender : M
Bill
Is that the sort of thing that you want?
(I realise that it's not precisely an IEnumerable<IGrouping<string, IEnumerable<IGrouping<string, Person>>>> but I figured that it was more the shape of the data that you were interested in that a precise type).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With