我是否可以使用LINQ和一条查询/LINQ语句将一个IEnumerable<T>
拆分为两个IEnumerable<T>
?
我想避免对IEnumerable<T>
进行两次迭代。例如,是否可以将下面的最后两条语句组合起来,使allValues只被遍历一次?
IEnumerable<MyObj> allValues = ...
List<MyObj> trues = allValues.Where( val => val.SomeProp ).ToList();
List<MyObj> falses = allValues.Where( val => !val.SomeProp ).ToList();
6条答案
按热度按时间4szc88ey1#
有些人喜欢字典,但我更喜欢查找,因为当一个键丢失时的行为。
不幸的是,这种方法枚举了两次-一次是创建查找,另一次是创建列表。
如果你真的不需要列表,你可以把它缩减到一次迭代:
0h4hbjxa2#
您可以使用此选项:
要像示例中那样强制立即求值,请执行以下操作:
gajydyqb3#
复制面食扩展方法以方便您。
或在C# 7.0中使用元组
z9zf31ra4#
现代C#示例只使用Linq,没有自定义扩展方法:
这是否回答了这个问题,是的;这比foreach更好或更可读吗,不。
rur96b6h5#
在所有这些答案中,你失去了LINQ的第二大功能(当然是在表达能力之后);懒惰!当我们调用
ToDictionary()
或ToLookup()
时,我们是在强制枚举。让我们看一下分区在Haskell中的实现,Haskell是一种很棒的懒惰函数式编程语言。
来自Hoogle:
正如你所看到的,分区是一个表达式,它返回两个其他表达式的元组。首先, predicate 被应用于元素,其次, predicate 的逆被应用于元素。在这种情况下,Haskell被隐式地延迟计算,类似于LINQ通过使用表达式而不是委托来延迟计算。
那么,为什么我们不以同样的方式实现我们的分区扩展方法呢?在LINQ中,filter被称为
where
,所以让我们使用它。一个警告是,如果你强制对匹配项AND进行求值,你将执行一个double枚举。但是,不要试图过早地优化。使用这种方法,你可以用LINQ来表示分区,从而保留它的有益特性。
bnl4lu3b6#
根据其他答案中的
ToLookup
建议,我很高兴地想到了这个扩展方法:调用站点如下所示:
我认为这是很好的可用性,这就是为什么我张贴这个答案。
我们可以更进一步:
predicate 的顺序对这个集合很重要。例如,如果按顺序传递
n => n > 5
和n => n > 100
,则第二个集合将始终为空。人们甚至可能渴望想出一个版本,它将与一个可变数量的 predicate (我知道我做到了),但据我所知,这是不可能的元组返回值在C#。