使用 LINQ 从 IGrouping 中选择特定元素

本文关键字:元素 选择 LINQ IGrouping 使用 | 更新日期: 2023-09-27 18:31:23

public class Item
{
    public int Id {get; set;}
    public bool Selected {get; set;}
}
List<Item> itemList = new List<Item>(){ /* fill with items */ };

我需要创建满足以下条件的项目列表。 从itemList,我需要按Id对项目进行分组,然后从每个组中选择一个项目。 所选项目必须是其中Selected == true 的项目。 如果组中没有选择任何项目,则可以选择任何项目,没关系,但必须选择一个。

基于这个问题:如何通过 Lambda 或 LINQ 从列表中获取不同的实例

我能够将以下内容放在一起,这似乎有效:

var distinctList = itemList.GroupBy(x => x.Id, 
    (key, group) => group.Any(x => x.Selected) ? 
        group.First(x => x.Selected) : group.First());

有没有更有效或更简单的方法来实现这一点? 我尝试了FirstOrDefault()但似乎无法让它做我需要的。 我对上述代码中效率的关注是对 Any() 的调用。

使用 LINQ 从 IGrouping 中选择特定元素

您确实可以使用 FirstOrDefault 扩展方法,但使用接受谓词的版本,并将其与合并运算符 ( ?? ) 组合在一起,如下所示(我在这里使用了查询语法以使其更容易):

var distinctList =
    from item in itemList
    group item by item.Id into g
    select g.FirstOrDefault(i => i.Selected) ?? g.First();

FirstOrDefault 中使用谓词,您将选取 Selected 属性为真的第一个项目。 如果没有,则假设您的类型是引用类型(这对于使用 FirstOrDefault 很重要),它将返回null

如果返回 null,则只需返回组中的第一项(通过调用 First ),因为任何项都可以返回,并且组中没有项无法存在(因此对First的调用始终保证成功)。

基本上,您正在将选择器应用于分组结果,而不是在分组时

你可以

利用bool实现IComparable<bool>的事实,true大于false

var distinctList = itemList.GroupBy(x => x.Id,  
    (key, group) => group.OrderByDescending(x => x.IsSelected).First()); 

但是,这需要对整个组进行枚举和排序,因此如果您坚持使用您的方法,您的表现会更好。

对于具有选定项的

组,您的方法需要迭代列表两次,直到第一个选定项的点。 您可以像这样删除该重复项:

var distinctList = itemList.GroupBy(x => x.Id,  
    (key, group) => group.FirstOrDefault(x => x.Selected) ?? group.First()); 

这里有更简单的答案

您可以使用

groupedSource.SelectMany(group => group).Single(item => item.Id == 1)