Home > Back-end >  Linq: pass GroupBy result to a function
Linq: pass GroupBy result to a function

Time:06-10

I need to group items in a list and then pass each group to a function for further elaboration. This is my code:

var list = new List<MyObj>(); // list is created ad populated elsewhere in code...

var query = list.AsEnumerable();

query = query.Where(x => x.MyProp == true).Select(x => x); // query definition is way more complicated

var grp = query.GroupBy(x => new { x.Name, x.Surname }).ToList();

Here grp is of type List<IGrouping<'a, MyObj>>.

I can easily iterate through my items with:

foreach (var g in grp)
{
    foreach (var o in g)
    {
        // here "o" is of type MyObj
    }
}

but I don't know how to create a function that receives a group and iterates through its items:

foreach (var g in grp)
{
    DoSomethingWithGroup(g);
}

This is because I have an anonymous type (Key) in grp definition.

I tried to replace the Key anonymous type with a custom type:

private class GrpKey
{
    public string Name { get; set; }
    public string Surname { get; set; }
}

/* ... */

var grp = query.GroupBy(x => new GrpKey { Name = x.Name, Surname = x.Surname }).ToList();

This way grp is of type List<IGrouping<MyGrpKey, MyObj>> instead of List<IGrouping<'a, MyObj>>.

I could then create a function:

private void DoSomethingWithGroup(IGrouping<MyGrpKey, MyObj>) { /* ... */ }

Unfortunately, this way grouping doesn't work anymore: grp now contains as many groups as items in the source list, each one with a single item.

CodePudding user response:

Instead of specifying your method like you did:

private void DoSomethingWithGroup(IGrouping<MyGrpKey, MyObj>) 

separate the key and the elements into their on parameters

private void DoSomethingWithGroup<T>(T groupKey, IEnumerable<MyObj> entities)

With this change you can do the following:

//Populate list with some dummy data
var list = new List<MyObj>()
{
    new MyObj { Id = 1, MyProp = true, Name = "A", Surname = "B"},
    new MyObj { Id = 2, MyProp = false, Name = "A", Surname = "B"},
    new MyObj { Id = 3, MyProp = true, Name = "B", Surname = "B"},
    new MyObj { Id = 4, MyProp = true, Name = "B", Surname = "B"},
    new MyObj { Id = 5, MyProp = true, Name = "C", Surname = "B"},
}; 

//Perform the grouping
var groups = list
   .Where(x => x.MyProp)
   .GroupBy(x => new { x.Name, x.Surname })
   .ToList();

//Perform some arbitrary action on the group basis
foreach (var group in groups)
{
    DoSomethingWithGroup(group.Key, group);
}

If the implementation of the DoSomethignWithGroup looks like this:

void DoSomethingWithGroup<T>(T groupKey, IEnumerable<MyObj> entities)
{
    Console.WriteLine(groupKey);
    foreach (var entity in entities)
    {
        Console.WriteLine($"- {entity.Id}");
    }
}

then the output will be this:

{ Name = A, Surname = B }
- 1
{ Name = B, Surname = B }
- 3
- 4
{ Name = C, Surname = B }
- 5

DotnetFiddle link

CodePudding user response:

If the key is not needed in your function, you can omit that in the method. A IGrouping<TKey, TElement> is an IEnumerable<TElement> so you could just define it as:

private void DoSomethingWithGroup(IEnumerable<MyObj> items)
{
   ///...
}
  • Related