2012-08-13 20 views
25

鉴于项目的集合,我如何基于谓词拆分成集2子集?LINQ本身是否支持将一个集合分为两部分?

你可以做2个在哪里搜索,但随后的运行时间为2 * N(其中,同时仍然为O(n),需要两倍长,显然不是首选)

IEnumerable<int> even = nums.Where(i => IsEven(i)); 
IEnumerable<int> odd = nums.Where(i => !IsEven(i)); 

你可以这样做一个单独的线性传递(这里重构为一个扩展方法),但这意味着你必须将这些代码全部拖拽到一起,而更多的自定义代码使得事情不易维护。

public static void SplitOnPred<T>(
     this IEnumerable<T> collection, 
     Func<T, bool> pred, 
     out IEnumerable<T> trueSet, 
     out IEnumerable<T> falseSet 
    ) { 
     List<T> trueSetList = new List<T>(); 
     List<T> falseSetList = new List<T>(); 
     foreach(T item in collection) { 
      if(pred(item)) { 
       trueSetList.Add(item); 
      } else { 
       falseSetList.Add(item); 
      } 
     } 
     trueSet = trueSetList; 
     falseSet = falseSetList; 
} 

问: LINQ是否有在1个通拆分收取任何原生支持?

+0

为什么你需要一个线性路径? – 2012-08-13 17:04:40

+3

@SaeedAmiri它不是一个真正的需求,以及2线性传递是在大多数情况下_good enough_但我从来没有与_good enough_的表现真的很开心:P – James 2012-08-13 17:06:49

回答

25

LINQ是否有在1个通拆分收取任何原生支持?

没有内建的方法根据谓词将集合拆分为两个版本。您需要使用自己的方法,类似于您发布的方法。

最接近内置的方法将是GroupBy(或ToLookup)。您可以通过组奇数或偶数:

var groups = nums.GroupBy(i => IsEven(i)); 

这将分成基础上的数字是否是奇数或偶数两个“团”。

5

那么,如果该逻辑是独家推出,在你的情况,你可以像

var list = new List<int> {1,2,3,4,5,6,7,8,9,10};  
var result = list.GroupBy(x=> x%2==0); 

result

foreach(var r in result) 
{ 
    if(r.Key) 
    //EVEN 
    else 
    //ODD 
} 
8

里德·科普塞的回答提到ToLookup,这似乎有吸引力。

var lookup = nums.ToLookup(IsEven); 

其中IsEven与预期的签名和返回类型的静态方法。然后

IEnumerable<int> even = lookup[true]; 
IEnumerable<int> odd = lookup[false]; 
1

如果你想支持延迟执行,使用功能或扩展这样的:

IEnumerable<T> Split<T>(this IEnumerable<T> source, out IEnumerable<T> odd) 
{ 
    IList<T> oddCollector = new List<T>(); 
    Bool odd = true; 
    foreach(T item in source) 
    { 
     if(odd) 
     { 
      oddCollector.Add(item); 
     } 
     else 
     { 
      yield return item; 
     } 
     odd = !odd; 
    } 
} 

我道歉,任何小型编译器错误,我这样做从我的头顶。您可以添加谓词,而不是偶数/奇数。

+0

警告这仅支持延迟执行,如果你无论是读甚至第一,或读两可交换顺序的枚举。如果你首先读到奇数,你将不会得到所有的结果。 – csauve 2016-11-11 16:40:07

相关问题