2016-06-30 1 views
0

collectionを特定の条件に一致するアイテムに分割したいと思います。TakeWhileとSkipWhileをパーティションコレクションに結合する

public static bool IsNotSeparator(int value) => value != 3; 

var collection = new [] { 1, 2, 3, 4, 5 }; 
var part1 = collection.TakeWhile(IsNotSeparator); 
var part2 = collection.SkipWhile(IsNotSeparator); 

をしかし、これは二回collectionの開始から繰り返すだろうとIsNotSeparatorが長くかかる場合、それはパフォーマンスの問題になる可能性があります:私は理解するのは簡単である、TakeWhileSkipWhileを使用していることを行うことができます。

より高速な方法は次のようなものを使用することです:

var part1 = new List<int>(); 
var index = 0; 
for (var max = collection.Length; index < max; ++index) { 
    if (IsNotSeparator(collection[i])) 
     part1.Add(collection[i]); 
    else 
     break; 
} 
var part2 = collection.Skip(index); 

しかし、それは最初の例よりも本当に少なく、より読みやすいです。

私の質問は:collectionを特定の要素に分割する最良の解決策は何ですか?

私は上記のこれら二つを組み合わせることですが、どう:

var collection = new [] { 1, 2, 3, 4, 5 }; 
var part1 = collection.TakeWhile(IsNotSeparator).ToList(); 
var part2 = collection.Skip(part1.Count); 
+1

これはむしろ主張されている質問です。 "ベストソリューション"は何を構成していますか?読みやすさ?コードのサイズ?速度?メモリ使用量? – Groo

+0

@Groo私は可読性のために行くだろう。しかし、 'IsNotSeparator'が長くかかると性能上問題になるかもしれません。 –

+0

リストはソートされていますか?これはパフォーマンスの最大の違いになります。また、 'Stringのように動作するように一般化すれば。「IEnumerable > Split(this IEnumerable list、Predicate match) 'などの拡張メソッドを作成できます。これは、指定された項目で区切られたチャンクをいくつでも返します。 – Groo

答えて

0

これは、あなたがLINQせずに、(複数の分割、コメントで述べたように)、より一般的な方法を行うだろうかの簡単な例です( )それは、LINQに変換することが可能ですが、私はそれがどんな読みやすくなりますかどうかわからない、と私は今、わずかに急いでいます:

public static IEnumerable<IEnumerable<T>> Split<T>(this IList<T> list, Predicate<T> match) 
{ 
    if (list.Count == 0) 
     yield break; 

    var chunkStart = 0; 
    for (int i = 1; i < list.Count; i++) 
    { 
     if (match(list[i])) 
     { 
      yield return new ListSegment<T>(list, chunkStart, i - 1); 
      chunkStart = i; 
     } 
    } 

    yield return new ListSegment<T>(list, chunkStart, list.Count - 1); 
} 

コードがより単純に反復するListSegment<T> : IEnumerable<T>という名前のクラスを想定していますfromto元のリスト(複製はありません。ArraySegment<T>の動作と似ていますが(残念ながら配列に限られます)。

マッチがあるように、コードは、このコード、つまり、できるだけ多くのチャンクを返します。

var collection = new[] { "A", "B", "-", "C", "D", "-", "E" }; 
foreach (var chunk in collection.Split(i => i == "-")) 
    Console.WriteLine(string.Join(", ", chunk)); 

は印刷されます:配列のコピー方法を使用しておよそ

A, B 
-, C, D 
-, E 
0

方法を:

var separator = 3; 
var collection = new [] { 1, 2, 3, 4, 5 }; 

var i = Array.IndexOf(collection,separator); 

int[] part1 = new int[i]; 
int[] part2 = new int[collection.Length - i]; 
Array.Copy(collection, 0, part1, 0, i); 
Array.Copy(collection, i, part2, 0, collection.Length - i); 

さらに効率的に使用するには、ArraySegment:

var i = Array.IndexOf(collection,separator); 
var part1 = new ArraySegment<int>(collection, 0, i); 
var part2 = new ArraySegment<int>(collection, i, collection.Length - i); 

ArraySegmentは、配列内の要素の範囲を区切る配列のラッパーです。複数のArraySegmentインスタンスは同じ元の配列を参照することができ、重なり合う可能性があります。

編集 - 元の質問の組み合わせをArraySegmentに追加して、コレクションを2回反復しないようにします。

public static bool IsNotSeparator(int value) => value != 3; 
var collection = new [] { 1, 2, 3, 4, 5 }; 

var index = collection.TakeWhile(IsNotSeparator).Count(); 

var part1 = new ArraySegment<int>(collection, 0, index); 
var part2 = new ArraySegment<int>(collection, index, collection.Length - index); 
+0

私はセパレータインデックスを事前に知っていれば簡単でしょう。私はどのインデックスを事前に分割する必要があるのか​​分からず、IsNotSeparatorメソッドで決定されます。 –

+0

@DovydasSopa確かにあなたは区切り文字を返すGetSeparatorメソッドとしてあなたのIsNotSeparatorを書き換えることができますし、私の答えに従ってArray.IndexOfメソッドを使うことができます –

関連する問題