2013-04-05 51 views
2

我想模仿通用陣列的String.Split功能。如何拆分通用陣列

我想出了這個方法,似乎適用於雙打。

public static double[][] Split(this double[] vals, double t) 
{ 
    List<double[]> ret = new List<double[]>(); 

    int last = -1; 
    for (int i = 0; i <= vals.Length; i++) 
    { 
     if (i != vals.Length && vals[i] != t) 
      continue; 

     int len = i - last - 1; 
     if (len <= 0) 
     { 
      last = i; 
      continue; 
     } 

     double[] arr = new double[len]; 
     Array.Copy(vals, last + 1, arr, 0, len); 
     last = i; 

     ret.Add(arr); 
    } 

    return ret.ToArray(); 
} 

而這其中仿製藥......

public static T[][] Split<T>(this T[] vals, T t) where T : EqualityComparer<T> 
{ 
    List<T[]> ret = new List<T[]>(); 

    int last = -1; 
    for (int i = 0; i <= vals.Length; i++) 
    { 
     if (i != vals.Length && vals[i] != t) 
      continue; 

     int len = i - last - 1; 
     if (len <= 0) 
     { 
      last = i; 
      continue; 
     } 

     T[] arr = new T[len]; 
     Array.Copy(vals, last + 1, arr, 0, len); 
     last = i; 

     ret.Add(arr); 
    } 

    return ret.ToArray(); 
} 

所以,我有三個問題:

  1. 是否有這樣做的更好/通用-C#的方式?
  2. 如何獲取模板方法的工作? (我得到vals[i] != t錯誤) - 現在固定
  3. 我怎樣才能做得更好(它現在是一種醜陋,IMO)

實例應用:

double[] vals = new double[] { 0, 1, 2, 0, 0, 2, 3, 0, 4, 5, 6 }; 
double[][] res = vals.Split(0); 

// res[0] = [1, 2] 
// res[1] = [2, 3] 
// res[2] = [4, 5, 6] 
+1

問題#1和#3是不是真的適合這裏。他們太主題了。在codereview上它可能沒問題,但它需要一些改進。它也需要先工作。 – Servy 2013-04-05 19:15:13

回答

2

您可以使用下面的通用擴展方法來通過某些分隔符分割序列。它使用默認比較器將每個項目與分隔符進行比較。

public static IEnumerable<T[]> Split<T>(this IEnumerable<T> source, T separator) 
{ 
    List<T> bucket = new List<T>(); 
    var comparer = Comparer<T>.Default; 

    foreach (var item in source) 
    { 
     if (comparer.Compare(item, separator) != 0) 
     { 
      bucket.Add(item); 
      continue; 
     } 

     if (bucket.Any()) 
     { 
      yield return bucket.ToArray(); 
      bucket = new List<T>(); 
     } 
    } 

    if (bucket.Any())   
     yield return bucket.ToArray();   
} 

用法:

double[] vals = new double[] { 0, 1, 2, 0, 0, 2, 3, 0, 4, 5, 6 }; 
double[][] res = vals.Split(0).ToArray(); 
+0

謝謝。我喜歡這個看起來/效果最好的方式。 – user807566 2013-04-05 19:43:15

2

如何我可以使用模板方法嗎? (我得到vals[i] != t錯誤)

!=運營商不會對任何任意類型T被定義。但是,你知道,T imlements IComparable<T>,所以槓桿作用:

vals[i].CompareTo(t) != 0 
+0

如何使用'IEquatable '或'IEqualityComparer '。 – 2013-04-05 19:18:05

+0

@ p.s.w.g兩者都是好主意。我專注於讓他的代碼工作所需的最小改變,而不是我自己如何實現它。 – Servy 2013-04-05 19:18:59

3

那麼,你可以懶洋洋地做到這一點,並且在任何序列,並作爲擴展方法。我也擺脫了IComparable<T>約束 - 你在這裏不使用。你可能使用CompareTo,而不是試圖使用!=(這可以告訴,不起作用),但因爲你只對平等感興趣,所以使用EqualityComparer<T>更有意義。

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, 
                T separator) 
{ 
    List<T> currentList = new List<T>(); 
    var comparer = EqualityComparer<T>.Default; 
    foreach (var item in source) 
    { 
     if (comparer.Equals(item, separator)) 
     { 
      yield return new ReadOnlyCollection<T>(currentList); 
      currentList = new List<T>(); 
     } 
     else 
     { 
      currentList.Add(item); 
     } 
    } 
    yield return new ReadOnlyCollection<T>(currentList); 
} 

注意,這返回空的集合,如果你有任何的開始或結束,或重複分隔符的分隔符。當然,你總是可以忽略那些在主叫方:

var nonEmptySequences = original.Split(value) 
           .Where(sequence => sequence.Any()); 

短,但完整的示例代碼:

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Linq; 

public static class MoreExtensions 
{ 
    public static IEnumerable<IEnumerable<T>> Split<T> 
     (this IEnumerable<T> source, T separator) 
    { 
     List<T> currentList = new List<T>(); 
     var comparer = EqualityComparer<T>.Default; 
     foreach (var item in source) 
     { 
      if (comparer.Equals(item, separator)) 
      { 
       yield return new ReadOnlyCollection<T>(currentList); 
       currentList = new List<T>(); 
      } 
      else 
      { 
       currentList.Add(item); 
      } 
     } 
     yield return new ReadOnlyCollection<T>(currentList); 
    } 

} 

class Test 
{ 
    static void Main() 
    { 
     int[] source = { 0, 1, 2, 0, 0, 2, 3, 0, 4, 5, 6 }; 
     foreach (var group in source.Split(0).Where(x => x.Any())) 
     { 
      Console.WriteLine("[{0}]", string.Join(",", group)); 
     } 
    }  
} 

輸出:

[1,2] 
[2,3] 
[4,5,6] 
+0

供參考他確保沒有空的條目,你不是。 – Servy 2013-04-05 19:18:10

+0

@Servy:正如我剛剛提到的:)('String.Split'不是默認情況下,並且很容易過濾出來...) – 2013-04-05 19:18:18