2009-06-18 4 views
7

Valueというオブジェクトのリストがあるとします。 ValueName性質を持っていますLinqを使用して連続して繰り返す要素を見つけよう

private List<Value> values = new List<Value> { 
    new Value { Id = 0, Name = "Hello" }, 
    new Value { Id = 1, Name = "World" }, 
    new Value { Id = 2, Name = "World" }, 
    new Value { Id = 3, Name = "Hello" }, 
    new Value { Id = 4, Name = "a" }, 
    new Value { Id = 5, Name = "a" }, 
}; 

は今、私はすべての「繰り返し」の値(nameプロパティは、前の要素のnameプロパティと一致した要素)のリストを取得したいです。
この例では、2つの要素 "world"と "a"(id = 2と5)を返すリストが必要です。

このイベントはlinqで可能ですか? もちろん、私はとてもかわいいかもしれません。このように:私は、より複雑な状況では、このクエリを使用したいので、

List<Value> tempValues = new List<Value>(); 
String lastName = String.Empty(); 
foreach (var v in values) 
{ 
    if (v.Name == lastName) tempValues.Add(v); 
    lastName = v.Name; 
} 

けど、多分「linqish」解決策があります。

答えて

7

あり、これらの線に沿って内蔵のものではありませんが、あなたはこれを必要とする場合、頻繁にあなたが特注何かをロールでしたが、かなり一般的:

static IEnumerable<TSource> WhereRepeated<TSource>(
    this IEnumerable<TSource> source) 
{ 
    return WhereRepeated<TSource,TSource>(source, x => x); 
} 
static IEnumerable<TSource> WhereRepeated<TSource, TValue>(
    this IEnumerable<TSource> source, Func<TSource, TValue> selector) 
{ 
    using (var iter = source.GetEnumerator()) 
    { 
     if (iter.MoveNext()) 
     { 
      var comparer = EqualityComparer<TValue>.Default; 
      TValue lastValue = selector(iter.Current); 
      while (iter.MoveNext()) 
      { 
       TValue currentValue = selector(iter.Current); 
       if (comparer.Equals(lastValue, currentValue)) 
       { 
        yield return iter.Current; 
       } 
       lastValue = currentValue; 
      } 
     } 
    } 
} 

使用法:

foreach (Value value in values.WhereRepeated(x => x.Name)) 
    { 
     Console.WriteLine(value.Name); 
    } 

あなたが望むかもしれませんがトリプレットなどで何をすべきか考えてみましょう。現在、最初のものを除くすべてが(あなたの説明に合った)収穫されますが、それは正しくないかもしれません。

+0

これはZipメソッドより効率的です。しかし、私は郵便番号のメソッドが少し良くなっていることを知っています(それは何よりもはっきりしています) –

+0

+1これは良い答えです –

+0

魅力のように動作します –

4

Zip extensionを実装し、リストを.Skip(1)で郵便番号を付けてから、一致する行を選択することができます。

これは動作し、維持することはかなり簡単なはず:

values 
    .Skip(1) 
    .Zip(items, (first,second) => first.Name==second.Name?first:null) 
    .Where(i => i != null); 

この方法のわずかな欠点は、あなたが二回リストを反復ということです。

+0

グレートソリューション要素を比較する機能を提供します。 パフォーマンスは私の場合は問題ではありません(わずか数百の要素)。 –

-1

これを行うにはGroupBy拡張機能を使用できます。

+1

いくつかのコードで詳しく説明できますか? –

1

私はこれがうまくいくと思います(これはテストされていません)。これは繰り返し単語とそのインデックスの両方を与えます。複数のリピートの場合、このリストをトラバースして連続するインデックスを確認できます。この

var dupsNames = 
    from v in values 
    group v by v.Name into g 
    where g.Count > 1 // If a group has only one element, just ignore it 
    select g.Key; 

よう

var query = values.Where((v,i) => values.Count > i+1 && v == values[i+1]) 
        .Select((v,i) => new { Value = v, Index = i }); 
+1

これは私にLINQyに感じることはありません... –

+0

ニース - 私は好きです:) @サム:何を意味するのは、LINQyではないのですか?それは私にとってかなりLINQyです:)(または、もし本当にtechincalを取得したいのなら、Lambday ..分割秒でLINQyにすることができます:)) –

+0

@Pure、値が純粋にIEnumerable(そしてIListではない)ならばこれはIListだけで動作する本当の特定のソリューションです。しかし、それは仕様にマッチし、仕事を完了させる。 –

-1

何かが動作するはずです。私は上記をテストしていないので、このキー=名前、値= {名前の要素}でグループ化

免責を返す必要があります

dupsNames.Select(d => values.Where(v => v.Name == d)) 

:あなたはその後、2番目のクエリで結果を使用することができます途方もないかもしれません。ここで

+1

これは、連続したリピートだけでなく、重複しているものをすべて取り出します。 – tvanfosson

1

は、IDは常にあなたのサンプルのようにシーケンシャルであれば動作するはず別の簡単な方法があります:

var data = from v2 in values 
      join v1 in values on v2.Id equals v1.Id + 1 
      where v1.Name == v2.Name 
      select v2; 
1

私はこの質問は古代である知っているが、私はちょうどので、同じことに取り組んでいた....

static class utils 
{ 
    public static IEnumerable<T> FindConsecutive<T>(this IEnumerable<T> data, Func<T,T,bool> comparison) 
    { 
     return Enumerable.Range(0, data.Count() - 1) 
     .Select(i => new { a=data.ElementAt(i), b=data.ElementAt(i+1)}) 
     .Where(n => comparison(n.a, n.b)).Select(n => n.a); 
    } 
} 

は何のために働く必要があります - あまりにも、

関連する問題