2010-09-21 100 views
2

讓我們假設我們有以下陣列集團通過元素Linq中

var arr = new string[] {"foo","bar","jar","\r","a","b,"c","\r","x","y","z","\r"); 

還忽略了一個事實,這是字符串,因此沒有字符串黑客的解決方案吧。

我想按序列中的每個「\ r」對這些元素進行分組。 也就是說,我想用「foo」,「bar」,「jar」和另一個與「a」,「b」,「c」等一個數組/ enumerable。

是否有任何可擴展的常式會讓我這樣做,還是我必須在這裏通過方法推出我自己的團隊?

+2

可能的重複[如何將IEnumerable 分成IEnumerable組的幾個組(http://stackoverflow.com/questions/1349491/how-can-i-split-an-ienumerablestring-into-groups-of -numeumerablestring) – James 2010-09-21 10:00:56

+3

不,它不是重複的 - 另一個是要求固定大小的組,這是要求基於分隔符的分割。 – Timwi 2010-09-21 10:10:33

+0

@Timwi:*可能*是關鍵字在這裏.... – James 2010-09-21 14:21:55

回答

4

我爲此寫了一個擴展方法,可以在任何IEnumerable<T>上使用。

/// <summary> 
/// Splits the specified IEnumerable at every element that satisfies a 
/// specified predicate and returns a collection containing each sequence 
/// of elements in between each pair of such elements. The elements 
/// satisfying the predicate are not included. 
/// </summary> 
/// <param name="splitWhat">The collection to be split.</param> 
/// <param name="splitWhere">A predicate that determines which elements 
/// constitute the separators.</param> 
/// <returns>A collection containing the individual pieces taken from the 
/// original collection.</returns> 
public static IEnumerable<IEnumerable<T>> Split<T>(
     this IEnumerable<T> splitWhat, Func<T, bool> splitWhere) 
{ 
    if (splitWhat == null) 
     throw new ArgumentNullException("splitWhat"); 
    if (splitWhere == null) 
     throw new ArgumentNullException("splitWhere"); 
    return splitIterator(splitWhat, splitWhere); 
} 
private static IEnumerable<IEnumerable<T>> splitIterator<T>(
     IEnumerable<T> splitWhat, Func<T, bool> splitWhere) 
{ 
    int prevIndex = 0; 
    foreach (var index in splitWhat 
     .Select((elem, ind) => new { e = elem, i = ind }) 
     .Where(x => splitWhere(x.e))) 
    { 
     yield return splitWhat.Skip(prevIndex).Take(index.i - prevIndex); 
     prevIndex = index.i + 1; 
    } 
    yield return splitWhat.Skip(prevIndex); 
} 

例如,在你的情況,你可以使用它像這樣:

var arr = new string[] { "foo", "bar", "jar", "\r", "a", "b", "c", "\r", "x", "y", "z", "\r" }; 
var results = arr.Split(elem => elem == "\r"); 

foreach (var result in results) 
    Console.WriteLine(string.Join(", ", result)); 

這將打印:

foo, bar, jar 
a, b, c 
x, y, z 

(包括在最後一個空白行,因爲有在收藏結束時是"\r")。

1

如果你想使用標準IEnumerable擴展方法,你必須使用Aggregate(但這不是可重複使用的Timwi的解決方案):

var list = new[] { "foo","bar","jar","\r","a","b","c","\r","x","y","z","\r" }; 
var res = list.Aggregate(new List<List<string>>(), 
         (l, s) => 
         { 
          if (s == "\r") 
          { 
           l.Add(new List<string>()); 
          } 
          else 
          { 
           if (!l.Any()) 
           { 
            l.Add(new List<string>()); 
           } 
           l.Last().Add(s); 
          } 
          return l; 
         }); 
0

看到這個nest yields to return IEnumerable<IEnumerable<T>> with lazy evaluation了。你可以有一個接受謂詞拆分SplitBy擴展方法:

public static IEnumerable<IList<T>> SplitBy<T>(this IEnumerable<T> source, 
               Func<T, bool> separatorPredicate, 
               bool includeEmptyEntries = false, 
               bool includeSeparators = false) 
{ 
    var l = new List<T>(); 
    foreach (var x in source) 
    { 
     if (!separatorPredicate(x)) 
      l.Add(x); 
     else 
     { 
      if (includeEmptyEntries || l.Count != 0) 
      { 
       if (includeSeparators) 
        l.Add(x); 

       yield return l; 
      } 

      l = new List<T>(); 
     } 
    } 

    if (l.Count != 0) 
     yield return l; 
} 

所以你的情況:

var arr = new string[] {"foo","bar","jar","\r","a","b,"c","\r","x","y","z","\r"); 
foreach (var items in arr.SplitBy(x => x == "\r")) 
    foreach (var item in items) 
    { 
    } 

同Timwi的,不同的方式實現。沒有錯誤檢查,這就是你。由於您只遍歷一次該列表,因此速度會更快。