Joseph
Joseph

Reputation: 522

LINQ Multiple Group By in a List<T> then convert to List<T>

I have the following class

public class Person
{
  public string ID {get; set;}
  public string Name {get; set;}
  public string District {get; set;}
  public string Level {get; set;}
}

Then on my function, I am writing a LINQ to group a list of Person, by District then by Level, into a new list of the same type (Person)

I want to write it in a way that i will not use var (making it an anonymous type) and new:

Something like

List<Person> persons; \\this already has values
List<Person> grpByP = persons
  .GroupBy()
  .ToList<Person>(); \\ Linq query to group persons list by District and Level

I just don't know how to write the grpByP LINQ query.

UPDATE: I want the ID and the Name be retained in the values of the grpByP variable because using anonymous type will only include the District and Level.

Any help will be appreciated.

Upvotes: 0

Views: 1403

Answers (3)

Konamiman
Konamiman

Reputation: 50273

The question for which this one had been originally marked as duplicate already explains how to use GroupBy. I'll address the second part: "converting to List<Person>" and "not using var".

First, about converting the result of GroupBy to List<Person>: you can't do that. The result of the GroupBy operation is a IEnumerable<IGrouping<String,Person>> (assuming you are grouping by a string property), which is itself an IEnumerable of IEnumerable<Person>; each of these inner IEnumerable<Person> corresponds to each of the keys found, and you can check the key for each case with the Key property:

var grouped = persons.GroupBy(p => p.District);
foreach(var group in grouped) 
{
    Console.WriteLine($"People in disctrict {group.Key}:");
    foreach(var person in group)
    {
        Console.WriteLine(person.Name);
    }
}

You could go back to get the full list of ungrouped people, but I'd say that doesn't make much sense:

var ungrouped = grouped.SelectMany(g => g).ToList<Person>();

And about not using var: you certainly could define the variable as IEnumerable<IGrouping<String,Person>>, but I don't see what would be the benefit of doing that.

Edit: All of this doesn't apply if as other answers suggest what you actually want to do is to sort your data, not to group it.

Edit per your comment: "I want the ID and the Name be retained in the values of the grpByP variable because using anonymous type will only include the District and Level" - GroupBy will not create anonymous objects, it will keep the original objects that you supplied (see my example above). Anonymous objects would be created only if you did something like .Select(p => new {p.Discrict}) in the resulting groups.

Upvotes: 0

Mick
Mick

Reputation: 6864

I think you're after something like this...

List<Person> persons = new List<Person>
{
    new Person()
    {
        ID = "1",
        Name = "Joe",
        District = "Columbia",
        Level = "10"
    },
    new Person()
    {
        ID = "2",
        Name = "Beth",
        District = "Columbia",
        Level = "10"
    },
    new Person()
    {
        ID = "3",
        Name = "Jim",
        District = "Washington",
        Level = "11"
    }
};  //this already has values
var grpByP = persons
    .GroupBy(p => new { p.District, p.Level })
    .Select(g => new
    {
        g.Key,
        People = g.ToList<Person>()
    });

foreach (var g in grpByP)
{
    Console.WriteLine("Group:");
    Console.WriteLine(g.Key.District);
    Console.WriteLine(g.Key.Level);
    Console.WriteLine("People:");
    foreach (Person p in g.People)
        Console.WriteLine(p.Name);
    Console.WriteLine();
}
Console.ReadLine();

Output:-

Group: Columbia 10 People: Joe Beth

Group: Washington 11 People: Jim

Upvotes: 2

Jamiec
Jamiec

Reputation: 136074

It sounds from comments like you just want to order your list by district, then by level. This is easy enough

List<Person> result = persons.OrderBy(p => p.District).ThenBy(p => p.Level).ToList();

Upvotes: 3

Related Questions