2009-05-01 4 views
4

は、私は文字列の配列を持っていると言う:C#、LINQを使用してマーカー間でアイテムを何度も何度も選ぶ方法は?

string[] strArray = {"aa", "bb", "xx", "cc", "xx", "dd", "ee", "ff", "xx","xx","gg","xx"}; 

は、どのように私はグループとして、「XX」マーカー間の文字列を抽出するためにLINQを使用していますか?あなたが最後のマーカーを過ぎて結果をフィルタリングしたい場合は

string[] strArray = { "aa", "bb", "xx", "cc", "xx", "dd", 
         "ee", "ff", "xx", "xx", "gg", "xx" }; 

var result = 
strArray.Aggregate((IEnumerable<IEnumerable<string>>)new IEnumerable<string>[0], 
    (a, s) => s == "xx" ? a.Concat(new[] { new string[0] }) 
     : a.Any() ? a.Except(new[] { a.Last() }) 
        .Concat(new[] { a.Last().Concat(new[] { s }) }) : a) 
     .Where(l => l.Any()); 

// Test 
foreach (var i in result) 
    Console.WriteLine(String.Join(",", i.ToArray())); 

cc 
dd,ee,ff 
gg 

答えて

16

純粋な官能性の溶液(突然変異フリーを):としてコンソールに書き込むことにより、

セイ:

string[] strArray = { "aa", "bb", "xx", "cc", "xx", "dd", 
         "ee", "ff", "xx", "xx", "gg", "xx"}; 

var result = 
    strArray.Aggregate(
    new { C = (IEnumerable<string>)null, 
      L = (IEnumerable<IEnumerable<string>>)new IEnumerable<string>[0] }, 
    (a, s) => s == "xx" ? a.C == null 
     ? new { C = new string[0].AsEnumerable(), a.L } 
     : new { C = new string[0].AsEnumerable(), L = a.L.Concat(new[] { a.C }) } 
     : a.C == null ? a : new { C = a.C.Concat(new[] { s }), a.L }).L 
      .Where(l => l.Any()); 

// Test 
foreach (var i in result) 
    Console.WriteLine(String.Join(",", i.ToArray())); 
+0

どのように世界でそれを理解しましたか?それは印象的です! +1 –

+0

@ジョーズ:ありがとう。しかし、それは難しかったです。それはちょっとしたハックです。あなたが何らかのループとしてAggregateを見ると、それは理にかなっています。 –

+2

+1邪悪なラムダカンフーです。実用的な目的のために、これはおそらく最も読み難いものではありませんが(それはTOO悪くはありません)、それでもそれでもなお非常に印象的です! –

-1

"xx"ではないものを選択できますが、見つかるたびに改行する必要がある場合あなたは、クエリではなく、(それぞれに対して)を使用する必要があります。 「XX」を抽出する

クエリは、リスト/配列をPartioning

from s from array 
where s != "xx" 
select s 
+1

問題は、このクエリでは、彼の要件に基づいて結果に含まれてはならないaa、bbも返されるということです。 –

0

だろうLINQはに特に適しているものではありません。 LINQ(完全に遅延型シーケンス)と互換性を持たせたい場合は、イテレータ(yieldキーワード)を使用してIEnumerable<IEnumerable<T>>を返す独自の拡張メソッドを記述することをお勧めします。遅延評価を気にしない場合は、配列を繰り返し処理してリストを生成し、最後にギザギザの配列(string[][]>)を返すメソッドを書くのが最も簡単です。

0

"xx"文字列に遭遇するたびに増加するグループカウンタを使用して、アイテムにグループ番号を割り当てることができます。その後、あなたは、「XX」の文字列をフィルタグループ番号のグループ、および空のグループをフィルタリング:

int group = 0; 
var lines = 
    strArray 
    .Select(s => new { Group = (s == "xx" ? ++group : group), Value = s }) 
    .Where(n => n.Value != "xx") 
    .GroupBy(n => n.Group) 
    .Where(g => g.Count() > 0); 

foreach (var line in lines) { 
    Console.WriteLine(string.Join(",", line.Select(s => s.Value).ToArray())); 
} 

編集:
このソリューションはまた、最初のマーカーの前の項目を削除し、最後のマーカーの後になります:

int group = 0; 
var lines = 
    strArray 
    .Select(s => new { Group = s == "xx" ? group++ : group, Value = s }) 
    .GroupBy(n => n.Group) 
    .Skip(1) 
    .Where(g => g.Last().Value == "xx" && g.Count() > 1); 

foreach (var line in lines) { 
    Console.WriteLine(string.Join(",", line.Take(line.Count() - 1).Select(s => s.Value).ToArray())); 
} 
+0

これは、OPが望みませんでした "aa"と "bb"を返しますが、グループ0とグループnをフィルタリングする方法はありますか?(nはグループの総数です - 1? –

+0

はい、各グループの最後の項目として、最初のグループをスキップし、マーカーで終わらないグループを除外します。私はそれを答えに加えました。 – Guffa

4

より良いアプローチは、一般的なIEnumerable<T>分割拡張メソッドを記述し、次に選択して、必要なその結果の一部を選択することであってもよいです。

public static class IEnumerableExtensions 
{ 
    public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
        this IEnumerable<TSource> source, TSource splitter) 
    { 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (splitter == null) 
     throw new ArgumentNullException("splitter"); 

    return source.SplitImpl(splitter); 
    } 

    private static IEnumerable<IEnumerable<TSource>> SplitImpl<TSource>(
        this IEnumerable<TSource> source, TSource splitter) 
    { 
    var list = new List<TSource>(); 

    foreach (TSource item in source) 
    { 
     if (!splitter.Equals(item)) 
     { 
     list.Add(item); 
     } 
     else if (list.Count > 0) 
     { 
     yield return list.ToList(); 
     list.Clear(); 
     } 
    } 
    } 
} 
そしてそう
static void Main(string[] args) 
{ 
    string[] strArray = { "aa", "bb", "xx", "cc", "xx", "dd", 
         "ee", "ff", "xx", "xx", "gg", "xx" }; 

    var result = strArray.Split("xx"); 
    foreach (var group in result.Skip(1).Take(3)) 
    { 
    Console.WriteLine(String.Join(",", group.ToArray())); 
} 

    Console.ReadKey(true); 
} 
のようにそれを使用して、あなたは、所望の出力
cc 
dd,ee,ff 
gg 
2

には、次の拡張メソッドを追加取得:

:ここ

public static class SplitExtensions { 
    public static IEnumerable<IEnumerable<T>> SplitBy<T>(this IEnumerable<T> src, T separator) { 
     var group = new List<T>(); 
     foreach (var elem in src){ 
      if (Equals(elem, separator)){ 
       yield return group; 
       group = new List<T>(); 
      } else{ 
       group.Add(elem); 
      } 
     } 
     yield return group; 
    } 
} 

はそれの使用であります

string[] strArray = { "aa", "bb", "xx", "cc", "xx", "dd", "ee", "ff", "xx", "xx", "gg", "xx" }; 
var groups = from g in strArray.SplitBy("xx") 
      where g.Any() 
      select g; 
関連する問題