209

I don't know if this is possible in Linq but here goes...

I have an object:

public class User
{
  public int UserID { get; set; }
  public string UserName { get; set; }
  public int GroupID { get; set; }
}

I return a list that may look like the following:

List<User> userList = new List<User>();
userList.Add( new User { UserID = 1, UserName = "UserOne", GroupID = 1 } );
userList.Add( new User { UserID = 2, UserName = "UserTwo", GroupID = 1 } );
userList.Add( new User { UserID = 3, UserName = "UserThree", GroupID = 2 } );
userList.Add( new User { UserID = 4, UserName = "UserFour", GroupID = 1 } );
userList.Add( new User { UserID = 5, UserName = "UserFive", GroupID = 3 } );
userList.Add( new User { UserID = 6, UserName = "UserSix", GroupID = 3 } );

I want to be able to run a Linq query on the above list that groups all the users by GroupID. So the output will be a list of user lists that contains user (if that makes sense?). Something like:

GroupedUserList
    UserList
        UserID = 1, UserName = "UserOne", GroupID = 1
        UserID = 2, UserName = "UserTwo", GroupID = 1
        UserID = 4, UserName = "UserFour", GroupID = 1
    UserList
        UserID = 3, UserName = "UserThree", GroupID = 2
    UserList
        UserID = 5, UserName = "UserFive", GroupID = 3
        UserID = 6, UserName = "UserSix", GroupID = 3

I've tried using the groupby linq clause but this seems to return a list of keys and its not grouped by correctly:

var groupedCustomerList = userList.GroupBy( u => u.GroupID ).ToList();
GEOCHET
  • 21,119
  • 15
  • 74
  • 98
lancscoder
  • 8,658
  • 8
  • 48
  • 68

5 Answers5

421
var groupedCustomerList = userList
    .GroupBy(u => u.GroupID)
    .Select(grp => grp.ToList())
    .ToList();
Lee
  • 142,018
  • 20
  • 234
  • 287
  • 2
    very nice, just what I needed. thank you. doing this the imperative way sucks. – user1841243 Dec 13 '13 at 21:53
  • 2
    Is it working fine? because I am used this ways didn't work. objModel.tblDonars.GroupBy(t => new { t.CreatedOn.Year, t.CreatedOn.Month, t.CreatedOn.Day }).Select(g => new { tblDonar = g.ToList() }).ToList(); this is not working... can you help.... – Rajamohan Anguchamy Apr 25 '14 at 09:57
  • with your solution it's work fine but i have one question suppose in the group up to 8 value and i want to just need in every group with just 6 take so how can do that please let me know. – coderwill Jun 28 '17 at 11:42
  • 1
    is there a way to list out UserNames and UserIDs separately after grouping by GroupID? like - {"1", [1, 2, 4], ["UserOne", "UserTwo", "UserFour"]} for groupID 1. – Nameless Feb 07 '18 at 14:29
  • Is it possible to group by two columns with this code? – FrenkyB May 11 '18 at 10:31
  • Thanks @Lee, from your solution I was able to solve a similar problem, which I had however using VB. [Grouping data with dynamic keys with Linq](https://stackoverflow.com/questions/50609558/grouping-data-with-dynamic-keys-with-linq/50613203#50613203) – Renan Barbosa May 30 '18 at 20:57
  • 1
    Surely a SelectMany will do this. grouping.SelectMany(sn => sn); – Jon H Oct 12 '18 at 15:35
  • Refer @JonSkeet's answer for next steps i.e. extracting data from a group. – Kishan Vaishnav Aug 01 '19 at 18:59
  • 7
    Currently the code doesn't work and causes an error if you try to cast it to previous type of list. Because returning a List in select creates a Lists inside a list which is not the desired output here. For those who have problems I can suggest : var groupedCustomerList = userList.GroupBy(u => u.GroupID).Select(grp => grp.First()).ToList(); – aliassce Feb 08 '20 at 21:28
44

Your group statement will group by group ID. For example, if you then write:

foreach (var group in groupedCustomerList)
{
    Console.WriteLine("Group {0}", group.Key);
    foreach (var user in group)
    {
        Console.WriteLine("  {0}", user.UserName);
    }
}

that should work fine. Each group has a key, but also contains an IGrouping<TKey, TElement> which is a collection that allows you to iterate over the members of the group. As Lee mentions, you can convert each group to a list if you really want to, but if you're just going to iterate over them as per the code above, there's no real benefit in doing so.

P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • The accepted answer didn't let me select the group `key` for some reason. So I had to change it like this: `var groupedCustomerList = userList.GroupBy(u => u.GroupID).Select(grp => grp).ToList();` – Ash K Oct 14 '22 at 21:38
5

For type

public class KeyValue
{
    public string KeyCol { get; set; }
    public string ValueCol { get; set; }
}

collection

var wordList = new Model.DTO.KeyValue[] {
    new Model.DTO.KeyValue {KeyCol="key1", ValueCol="value1" },
    new Model.DTO.KeyValue {KeyCol="key2", ValueCol="value1" },
    new Model.DTO.KeyValue {KeyCol="key3", ValueCol="value2" },
    new Model.DTO.KeyValue {KeyCol="key4", ValueCol="value2" },
    new Model.DTO.KeyValue {KeyCol="key5", ValueCol="value3" },
    new Model.DTO.KeyValue {KeyCol="key6", ValueCol="value4" }
};

our linq query look like below

var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList() };

or for array instead of list like below

var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList().ToArray<string>() };
Usman Maqbool
  • 3,351
  • 10
  • 31
  • 48
sangram
  • 377
  • 4
  • 5
0

Still an old one, but answer from Lee did not give me the group.Key as result. Therefore, I am using the following statement to group a list and return a grouped list:

public IOrderedEnumerable<IGrouping<string, User>> groupedCustomerList;

groupedCustomerList =
        from User in userList
        group User by User.GroupID into newGroup
        orderby newGroup.Key
        select newGroup;

Each group now has a key, but also contains an IGrouping which is a collection that allows you to iterate over the members of the group.

datapool
  • 203
  • 3
  • 12
  • This answers *your* question, not OP's question. They only want a list of lists. Your code doesn't provide that. – Gert Arnold Nov 03 '19 at 16:37
  • I have posted this solution because the accepted answer above from @Lee does not work when iterating as it does _not_ provide the `group.key` element when iterating through the grouped list like shown in answer by @JohnSkeet. My provided answer _does_ provide the group.key element when iterating. So it might help other falling into the trap that the provided answers do not work as shown above. So it is just another way to get the list with the benefit of getting the group.key element similar to the accepted answer. Dont understand your claim @GertArnold. (.net core 3.0) – datapool Nov 13 '19 at 08:21
  • I think the whole question is based on lack of understanding what `IGrouping` is. That's why OP discard their own correct code at the bottom, that is *essentially identical* to your code. That's why they accept an answer that provides exactly what they ask for. What they need is an explanation, which JS sufficiently provided. – Gert Arnold Nov 13 '19 at 16:45
-3

Nowadays you can use Linq's FindAll()

var groupedCustomerList = userList.FindAll( u => u.GroupID);
Rodlaiz
  • 5
  • 1
  • What is "nowadays" about `FindAll`? Also, strictly this is not a LINQ method. It's a `List` instance method. The main objection against your answer, though, is that it has nothing to do with grouping. – Gert Arnold Jul 27 '22 at 18:29