如何使用LINQ同时返回满足/不满足比较器条件的两个IEnumerable?
解答
原生LINQ没有提供开箱即用的直接拆分两个序列的方法,但有两个非常实用的实现方案,都能满足你的需求,其中第一种性能最好,只会遍历一次源数据。
方案1:自定义扩展方法(生产环境推荐)
自己写个通用的拆分扩展方法是最常用的做法,单次遍历就能完成分组,不会有重复遍历的开销,代码也很简单:// 把这个静态方法放到你项目里任意一个静态工具类中即可全局调用 public static (IEnumerable<T> Passed, IEnumerable<T> Failed) SplitBy<T>( this IEnumerable<T> source, Func<T, bool> predicate) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); var passList = new List<T>(); var failList = new List<T>(); foreach (var item in source) { if (predicate(item)) passList.Add(item); else failList.Add(item); } return (passList, failList); }你题目里待补充的代码直接调用这个扩展就行:
var (listWhereTrue, listWhereFalse) = numbers.SplitBy(comparator);运行结果和预期完全一致:
listWhereTrue包含 0、1、2,listWhereFalse包含3、4、5。方案2:用内置
ToLookup方法临时实现
要是只是写个临时脚本、不想额外加扩展方法,直接用LINQ内置的ToLookup就能实现,同样只遍历一次源序列:var groups = numbers.ToLookup(comparator); // 直接解构赋值的写法如下 var (listWhereTrue, listWhereFalse) = (groups[true].ToList(), groups[false].ToList());这个方法返回的是分组查询结果,如果不需要立刻转成实体列表,去掉
.ToList()也可以直接用。
避坑提示:别图省事写两次
Where分别筛选,也就是分别写numbers.Where(comparator)和numbers.Where(x => !comparator(x)),这种写法会把源序列遍历两遍,数据量大的时候性能差很多,如果遍历过程有副作用逻辑还会出意料之外的bug。
内容的提问来源于stack exchange,提问作者user18908005
相关产品推荐
相关产品推荐

