2012-07-11 66 views
4

我想要一個將謂詞拆分IEnumerable的方法,通過它們的索引將謂詞相對於謂詞分組。例如,它可以在滿足x => MyRegex.Match(x).Success的項目上拆分List<string>,其中這些匹配項之間的項目被分組在一起。根據謂詞拆分LINQ查詢

其標誌可能看起來線

public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate, 
    int bool count 
) 

,可能與包含所有分頻器的輸出的一個額外的元素。

有沒有比foreach循環更高效和/或更緊湊的方式來實現?我覺得應該可以用LINQ方法來實現,但是我不能把它放在手指上。

示例:以下的

string[] arr = {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"}; 
arr.Split(x => x.EndsWith("e")); 

要麼是OK:

IEnumerable<string> {{}, {"Two"}, {}, {"Four", "Seven"}, {}} 
IEnumerable<string> {{"Two"}, {"Four", "Seven"}} 

用於存儲匹配的可選元素將是{"One", "Three", "Nine", "Five"}

+7

你的意思是「group by」以外的東西? – 2012-07-11 17:36:04

+0

「split」是什麼意思?返回兩個列表,一個匹配,一個不匹配? – 2012-07-11 17:36:30

+0

是的 - 它不會根據物品的特徵進行分割,而是基於定位。 – Arithmomaniac 2012-07-11 17:37:34

回答

3

您應該通過一個擴展方法做到這一點(這個方法可以讓你忽略了分區項):

/// <summary>Splits an enumeration based on a predicate.</summary> 
/// <remarks> 
/// This method drops partitioning elements. 
/// </remarks> 
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> partitionBy, 
    bool removeEmptyEntries = false, 
    int count = -1) 
{ 
    int yielded = 0; 
    var items = new List<TSource>(); 
    foreach (var item in source) 
    { 
     if (!partitionBy(item)) 
      items.Add(item); 
     else if (!removeEmptyEntries || items.Count > 0) 
     { 
      yield return items.ToArray(); 
      items.Clear(); 

      if (count > 0 && ++yielded == count) yield break; 
     } 
    } 

    if (items.Count > 0) yield return items.ToArray(); 
} 
+0

我*認爲*如果有兩個或更多滿足'partitionBy()'的連續項目,這將返回空插入式數組。 – 2012-07-11 17:48:37

+1

@FrédéricHamidi:是的,他的例子包括兩個空蕩蕩的傢伙。 – user7116 2012-07-11 17:49:24

+0

哈,你說得對,它現在就行。我的壞:) – 2012-07-11 17:51:03

3
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate) 
{ 
    List<TSource> group = new List<TSource>(); 
    foreach (TSource item in source) 
    { 
     if (predicate(item)) 
     { 
      yield return group.AsEnumerable(); 
      group = new List<TSource>(); 
     } 
     else 
     { 
      group.Add(item); 
     } 
    } 
    yield return group.AsEnumerable(); 
} 
+0

是否有任何理由在此列表中調用'AsEnumerable'? – 2013-09-30 16:48:37

+0

@DrewNoakes沒有。 – Servy 2013-09-30 16:51:46

10

如果你正在尋找避免擴展方法,你總是可以使用:

var arr = new[] {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"}; 

var result = arr.ToLookup(x => x.EndsWith("e")); 

// result[true] == One Three Nine Five 
// result[false] == Two Four Seven 
+0

這是一個巧妙的技巧,但不保留通過拆分分組的真實項目。 – Arithmomaniac 2013-08-13 04:29:55

+0

謝謝,這幫了我一個類似的問題 – 2014-03-28 03:23:06

+0

這是一個很好的解決方案。另外,我們可以用積極的詞語來命名變量。在Andy的例子中,將變量命名爲'itemsEndWithE',所以'itemsEndWithE [false]'意味着項目不以E結尾。我們不需要額外的變量來表示它。 – Gqqnbig 2017-06-07 01:00:57

1
public static IEnumerable<IEnumerable<TSource>> Partition<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{ 
    yield return source.Where(predicate); 
    yield return source.Where(x => !predicate(x)); 
} 

實施例:

var list = new List<int> { 1, 2, 3, 4, 5 }; 
var parts = list.Partition(x => x % 2 == 0); 
var even = parts.ElementAt(0); // contains 2, 4 
var odd = parts.ElementAt(1); // contains 1, 3, 5