2010-09-21 61 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的,不同的方式实现。没有错误检查,这就是你。由于您只遍历一次该列表,因此速度会更快。