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

时间:2012-04-16 20:45:54

标签: c# linq

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()的调用。

3 个答案:

答案 0 :(得分:3)

您确实可以使用FirstOrDefault extension method,但使用version that takes a predicate,并将其与coalesce operator (??)结合使用(我在此处使用了查询语法,以便更轻松):< / p>

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),因为任何项目都可以返回,并且组不能存在没有< / em>中的项目(因此始终保证对First的调用成功)。

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

答案 1 :(得分: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()); 

答案 2 :(得分:0)

更简单的回答here

您可以使用

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