使用 Linq 将一个对象列表分组为一个新的对象列表组成的列表

211

我不知道在 LINQ 中是否可能,但是以下是我的问题:

我有一个对象:

public class User
{
  public int UserID { get; set; }
  public string UserName { get; set; }
  public int GroupID { get; set; }
}
我返回的列表可能看起来像以下这样:
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 } );

我希望能够在上述列表上运行一个Linq查询,将所有用户按GroupID分组。因此输出将是包含用户列表的用户列表(如果这有意义的话?)。类似于:

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

我尝试使用groupby linq语法,但是它似乎返回一个键的列表,并且没有正确地分组:

var groupedCustomerList = userList.GroupBy( u => u.GroupID ).ToList();
5个回答

421
var groupedCustomerList = userList
    .GroupBy(u => u.GroupID)
    .Select(grp => grp.ToList())
    .ToList();

2
非常好,正是我所需要的。谢谢你。用命令式方式做这件事很糟糕。 - user1841243
2
它正常工作吗?因为我用这种方法不起作用。objModel.tblDonars.GroupBy(t => new { t.CreatedOn.Year, t.CreatedOn.Month, t.CreatedOn.Day }).Select(g => new { tblDonar = g.ToList() }).ToList(); 这个不起作用...你能帮忙吗? - Rajamohan Anguchamy
1
在按GroupID分组后,有没有一种方法可以将用户名和用户ID分别列出来?例如 - 对于groupID 1,{"1",[1,2,4],["UserOne","UserTwo","UserFour"]}。 - Nameless
1
肯定是使用SelectMany来完成这个任务。grouping.SelectMany(sn => sn); - Jon H
7
目前这段代码无法运行,如果您试图将其转换回之前的列表类型,会导致错误。因为在select语句中返回一个List会创建一个嵌套的列表,而这并不是期望的输出结果。对于那些遇到问题的人,我可以建议使用以下代码: var groupedCustomerList = userList.GroupBy(u => u.GroupID).Select(grp => grp.First()).ToList(); - aliassce
显示剩余4条评论

43

你的group语句将会按照group ID进行分组。例如,如果你接着写:

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

这应该可以很好地工作。每个组都有一个键,但也包含一个IGrouping<TKey, TElement>,它是一个允许您迭代组成员的集合。正如Lee所提到的,如果您确实想要,您可以将每个组转换为列表,但如果您只是按照上面的代码遍历它们,那么这样做并没有真正的好处。


被接受的答案由于某种原因没有让我选择组“key”。所以我不得不像这样更改它:var groupedCustomerList = userList.GroupBy(u => u.GroupID).Select(grp => grp).ToList(); - Ash K

5
对于类型
public class KeyValue
{
    public string KeyCol { get; set; }
    public string ValueCol { get; set; }
}

集合

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" }
};

我们的Linq查询如下所示。
var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList() };

或者使用数组而不是像下面这样的列表
var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList().ToArray<string>() };

0

虽然这是一个老问题,但是Lee的回答没有给我group.Key作为结果。因此,我使用以下语句对列表进行分组并返回分组后的列表:

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

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

每个组现在都有一个键,但也包含一个 IGrouping,它是一个集合,允许您迭代组的成员。

这回答了你的问题,而不是OP的问题。他们只想要一个列表的列表。你的代码没有提供那个。 - Gert Arnold
我发布了这个解决方案,因为上面被接受的答案(来自@Lee)在迭代时不起作用,因为它在遍历分组列表时没有提供group.key元素,就像@JohnSkeet的答案所示。我的答案在迭代时提供了group.key元素。因此,它可能会帮助其他人避免落入所提供的答案不起作用的陷阱。因此,这只是另一种获取列表的方法,可以获得类似于接受的答案的group.key元素的好处。不理解您的声明@GertArnold。 (.net core 3.0) - datapool
我认为整个问题基于对 IGrouping 的不理解。这就是为什么 OP 在底部放弃了他们自己的正确代码,这与您的代码本质上相同。这就是为什么他们接受一个提供了他们所要求的答案的回答。他们需要的是解释,JS已经充分提供了。 - Gert Arnold

-3

现在你可以使用 Linq 的 FindAll() 方法。

var groupedCustomerList = userList.FindAll( u => u.GroupID);

“现在”关于FindAll有什么问题?严格来说,这不是一个LINQ方法,而是一个List<T>实例方法。然而,你的回答主要问题在于它与分组无关。 - Gert Arnold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接