291

I have a question about IGrouping and the Select() method.

Let's say I've got an IEnumerable<IGrouping<int, smth>> in this way:

var groups = list.GroupBy(x => x.ID);

where list is a List<smth>.

And now I need to pass values of each IGrouping to another list in some way:

foreach (var v in structure)
{
    v.ListOfSmth = groups.Select(...); // <- ???
}

Can anybody suggest how to get the values (List<smth>) from an IGrouping<int, smth> in such a context?

CarenRose
  • 1,266
  • 1
  • 12
  • 24
Illia Ratkevych
  • 3,507
  • 4
  • 29
  • 35
  • ID is usually an identity field which should be unique, which would make grouping by it useless, if your just trying to remove duplicate data try Distinct() instead. if it was list.GroupBy(x => x.SubID) then it would make sense to use the grouping but in that case you would most likely want to keep the grouping and foreach(var grp in groups){grp.ToList() ; } would do that – MikeT Nov 18 '13 at 18:03

8 Answers8

356

Since IGrouping<TKey, TElement> implements IEnumerable<TElement>, you can use SelectMany to put all the IEnumerables back into one IEnumerable all together:

List<smth> list = new List<smth>();
IEnumerable<IGrouping<int, smth>> groups = list.GroupBy(x => x.id);
IEnumerable<smth> smths = groups.SelectMany(group => group);
List<smth> newList = smths.ToList();

Here's an example that builds/runs: https://dotnetfiddle.net/DyuaaP

Video commentary of this solution: https://youtu.be/6BsU1n1KTdo

Matt Smith
  • 17,026
  • 7
  • 53
  • 103
  • 5
    Isn't that the same as a normal "orderBy"? List newList = list .OrderBy(x => x.id).ToList(); – MorgoZ Apr 22 '16 at 11:42
  • @MorgoZ, the question is about taking the `groups` and getting back to a flat list. – Matt Smith Apr 22 '16 at 14:09
  • 4
    @MorgoZ, `OrderBy(x => x.id)` will sort them in ascending order by ID. Contrast this with `.GroupBy(x => x.id).SelectMany(group => group)`, which will sort them by order of first appearance of ID. If the original IDs are in the order: [1,3,2,2,2,3,0], then grouping them and then flattening into one list will put the IDs in the new order: [1,3,3,2,2,2,0]. – Pi Fisher Apr 03 '17 at 18:05
  • 4
    It does't work for me.. it says that the type cannot be inferred by the usage – Revious Dec 15 '17 at 16:48
  • 5
    type cannot be inferred by the usage – Leo Gurdian Jun 14 '18 at 00:55
  • @Revious Do you have an example that doesn't work? Here's an example that builds/runs: https://dotnetfiddle.net/DyuaaP – Matt Smith Jun 08 '20 at 20:45
  • 2
    @zcoop98, no. That does not work. That will give you the first element in each of the groups which is not the desired result: https://dotnetfiddle.net/sXwnGM – Matt Smith Mar 26 '21 at 13:57
  • @MattSmith You're completely right, I was solving a different problem. When I wrote that, I was trying to find out how to take a list and remove objects with duplicate IDs, which is exactly what that `.Select(x => x.FirstOrDefault());` statement does in this case: https://dotnetfiddle.net/o4m4lR. But that's not what this question is asking, which I failed see. – zcoop98 Mar 26 '21 at 14:53
  • 1
    This just doesn't make sense. `SelectMany` will reverse `GroupBy` so it's not what desired and your `newList` is exactly the same as the `list`. – montonero Dec 14 '21 at 09:26
  • That wasn't the question. – Kim Homann May 04 '22 at 09:52
  • This is the highest scored answer, but it does not answer the question. With all due respect I will have to downvote this. – Cliff Pennalligen Nov 15 '22 at 07:48
48
foreach (var v in structure) 
{     
    var group = groups.Single(g => g.Key == v. ??? );
    v.ListOfSmth = group.ToList();
}

First you need to select the desired group. Then you can use the ToList method of on the group. The IGrouping is a IEnumerable of the values.

aloisdg
  • 22,270
  • 6
  • 85
  • 105
Tim Cools
  • 1,162
  • 10
  • 19
  • This answer is correct, but I think the question is already wrong. Why convert your `IEnumerable` to a `List` in the first place? – Kim Homann May 04 '22 at 09:54
26

More clarified version of above answers:

IEnumerable<IGrouping<int, ClassA>> groups = list.GroupBy(x => x.PropertyIntOfClassA);

foreach (var groupingByClassA in groups)
{
    int propertyIntOfClassA = groupingByClassA.Key;

    //iterating through values
    foreach (var classA in groupingByClassA)
    {
        int key = classA.PropertyIntOfClassA;
    }
}
Bronek
  • 10,722
  • 2
  • 45
  • 46
18

From definition of IGrouping :

IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable

you can just iterate through elements like this:

IEnumerable<IGrouping<int, smth>> groups = list.GroupBy(x => x.ID)
foreach(IEnumerable<smth> element in groups)
{
//do something
}
user1275513
  • 357
  • 2
  • 8
11
var groups = list.GroupBy(x => x.ID);

Can anybody suggest how to get the values (List) from an IGrouping<int, smth> in such a context?

"IGrouping<int, smth> group" is actually an IEnumerable with a key, so you either:

  • iterate on the group or
  • use group.ToList() to convert it to a List
foreach (IGrouping<int, smth> group in groups)
{
   var thisIsYourGroupKey = group.Key; 
   List<smth> list = group.ToList();     // or use directly group.foreach
}
SA-
  • 141
  • 2
  • 9
  • Google shows many examples like this, where the enumerating is done by a foreach loop. But the question was how to get the list of items itself. – Kim Homann May 04 '22 at 09:56
4

If you have an IGrouping<GroupItem, ListItem>, and you want to access the items of type ListItem of this group without utilizing a foreach loop, it's very simple. The object of type IGrouping<GroupItem, ListItem> is of type IEnumerable<ListItem> as well, as it is defined as:

public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable

So you can simply say:

foreach (IGrouping<GroupItem, ListItem> group in list.GroupBy(x => x.ID))
{
    IEnumerable<ListItem> itemsInThisGroup = group;
    // ...
}

If for some reason, it has to be a List<T> instead of an IEnumerable<T>, you can of course still call itemsInThisGroup.ToList(). But usually it's better not to if you needn't.

Kim Homann
  • 3,042
  • 1
  • 17
  • 20
-1

Simply do this:

// this will "split" the list into groups
var groups = list.GroupBy(x => x.ID);

// groups is a "collection of lists"
foreach (var sublist in groups)
{
  // now the sublist is only a part of the original list
  // to get which is the value of ID, you can use sublist.Key
}

You don't need Select().GroupBy(expr) makes "a list of lists", kind of.

Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
-2

Assume that you have MyPayments class like

 public class Mypayment
{
    public int year { get; set; }
    public string month { get; set; }
    public string price { get; set; }
    public bool ispaid { get; set; }
}

and you have a list of MyPayments

public List<Mypayment> mypayments { get; set; }

and you want group the list by year. You can use linq like this:

List<List<Mypayment>> mypayments = (from IGrouping<int, Mypayment> item in yearGroup
                                                let mypayments1 = (from _payment in UserProjects.mypayments
                                                                   where _payment.year == item.Key
                                                                   select _payment).ToList()
                                                select mypayments1).ToList();
ayberkzeray
  • 205
  • 1
  • 2
  • 10