使用 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() 的调用。
您确实可以使用 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)