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

3
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的项目。如果组中没有选择任何项目,则可以选择任意项目,但必须选择一个。
基于这个问题: How to get distinct instance from a list by Lambda or LINQ 我能够提出以下代码,似乎可以达到上述要求:
var distinctList = itemList.GroupBy(x => x.Id, 
    (key, group) => group.Any(x => x.Selected) ? 
        group.First(x => x.Selected) : group.First());

是否有更高效或更简单的方法来实现这一点?我尝试了FirstOrDefault()但似乎不能满足我的需求。我关心上述代码中调用Any()的效率问题。


这对你来说不够简单吗?添加一些额外的换行符和/或删除一些缩进,以便它更适合屏幕显示,我觉得看起来很好。 - Servy
@Servy -- 我不明白为什么会被踩?下面的答案比我上面的例子更简单、更高效。这就是我提出问题的原因。 - J. Andrew Laughlin
3个回答

3
您确实可以使用FirstOrDefault扩展方法,但要使用带有谓词的版本,并将其与空合并运算符(??)结合使用,像这样(我在此处使用了查询语法使其更容易):
var distinctList =
    from item in itemList
    group item by item.Id into g
    select g.FirstOrDefault(i => i.Selected) ?? g.First();

使用 FirstOrDefault 中的谓词,您正在选择第一个 Selected 属性为 true 的项目。 如果没有,则假定您的类型是引用类型(这对于与 FirstOrDefault 一起使用非常重要),它将返回 null
如果返回 null,则只需通过调用 First 返回组中的第一个项目,因为可以返回任何项目,而且组不能存在没有其中项目(因此调用 First 总是保证成功)。
基本上,您正在将选择器应用于分组的 结果,而不是在分组应用。

0

您可以利用 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()); 

0

更简单的答案 在这里

你可以使用

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

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