Question

How would I go about grouping a list of objects by two different fields?

e.g.

struct Item
{
    public string Name;
    public int Age;
    public int Score;
}

List<Item> items = new List<Item>();
items.Add(new Item(){ "Jeff", 35, 50};
items.Add(new Item(){ "Annie", 22, 45};
items.Add(new Item(){ "Pierce", 60, 90};
items.Add(new Item(){ "Pierce", 60, 10};
items.Add(new Item(){ "Abed", 28, 22};
items.Add(new Item(){ "Troy", 22, 45};
items.Add(new Item(){ "Troy", 22, 65};
items.Add(new Item(){ "Troy", 22, 80};
items.Add(new Item(){ "Troy", 21, 2};
items.Add(new Item(){ "Troy", 21, 5};

var grouped = items.GroupBy(/*Group by age then name*/);

grouped would then be:

"Troy", 21, 2
"Troy", 21, 5

"Annie", 22, 45

"Troy", 22, 80
"Troy", 22, 65
"Troy", 22, 45

"Abed", 28, 22

"Jeff", 35, 50

"Pierce", 60, 90
"Pierce", 60, 10

Most solutions I've found deal with creating a new anonymous type, but that would surely lose the "Score" field.

How do I create a grouping of the original objects where the objects are grouped by multiple criteria?

Was it helpful?

Solution

var grouped = items.GroupBy(item => new { item.Name, item.Age });

This will produce an IGrouping<TKey, TElement> where TKey is the anonymous type and the TElement is Item. Basically, this is an IEnumerable of IEnumerable.

You can access them with a double for-loop:

foreach (var group in grouped) {
    foreach(Item item in group) {
        //...
    }
}

OTHER TIPS

GroupBy method returns a collection of IGrouping<(Of <(TKey, TElement>)>) objects, one for each distinct key that was encountered. The key represents the attribute that is common to each value in the IGrouping<(Of <(TKey, TElement>)>) and can be accessed using a ForEach loop.

  var qry = items.GroupBy(item => new { item .Age, item .Name },
           (key, group) => new
           {
               Key1 = key.Age,
               Key2 = key.Name,
               All = group.ToList()
           });

You can check the result with this:

qry.ToList().ForEach(x => x.All.ForEach(y=>Console.WriteLine("{0}, {1}, {2}",y.Name, y.Age, y.Score)));

Use this:

var grouped = items.GroupBy(item => item new{ item.Name, item.Age });

You will not loose the Score field. sublists of grouped list will still be of type Item and objects of anonymous type will be in Key property of each group.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top