LINQ本身是否支持将一个集合拆分为两个?

时间:2012-08-13 17:00:07

标签: c# linq

给定项目集合,如何根据谓词将集合拆分为2个子集合?

你可以做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个线性传递中拆分集合?

4 个答案:

答案 0 :(得分:27)

  

LINQ是否有任何本机支持在1个线性传递中拆分集合?

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

最接近的内置方法是GroupBy(或ToLookup)。你可以按奇数或偶数分组:

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

根据数字是奇数还是偶数,这将分成两个“组”。

答案 1 :(得分:8)

Reed Copsey的回答提到ToLookup,这似乎很有吸引力。

var lookup = nums.ToLookup(IsEven);

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

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

答案 2 :(得分: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
}

答案 3 :(得分: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;
   }
 }

我对任何小编译器错误表示歉意,我是从头脑中做到的。您可以添加谓词,而不是偶数/奇数。