2009-06-09 15 views
0

シーケンスの最後の項目以外のすべてを私に渡す方法が必要でした。これは私の現在の実装です:C#:SkipLastの実装

私が必要なのは、最後のものを除くすべての項目で何かをすることです。私の場合、私はさまざまなプロパティを持つオブジェクトのシーケンスを持っています。その後、日付順に注文してから、最後のアイテム(注文後の最後のアイテム)以外のすべてのアイテムを調整する必要があります。

私はこれらの列挙子や物事にまだ参加していませんし、実際には誰にも聞いてもらえません:pこれは良い実装か、どこか大きな打撃。もしかしたら、この問題が奇妙なものなのかな?

もっと一般的な実装はAllExceptMaxByの方法でした。それはそれが何であるかのようなものなので。 MoreLinqにはMaxByMinByの方法があり、同じ方法で処理する必要がありますが、最大値または最小値以外のすべての値を返します。

答えて

8

「最後の要素」はマルコフの停止点ではないため、次の要素を取得しようとするまで、最後の要素に到達したとは言えません。それは実行可能ですが、あなたが永遠に "一要素の背後にある"気にならない場合に限ります。これは基本的にあなたの現在の実装がしていることですが、それは大丈夫ですが、私はおそらくそれを少し違った形で書くでしょう。

別のアプローチは、あなたが最初の反復にあった場合を除き、常に以前に返された値が得られ、foreachを使用することです:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source) 
{ 
    T previous = default(T); 
    bool first = true; 
    foreach (T element in source) 
    { 
     if (!first) 
     { 
      yield return previous; 
     } 
     previous = element; 
     first = false; 
    } 
} 

別のオプション、あなたのコードに近い:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source) 
{ 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     if(!iterator.MoveNext()) 
     { 
      yield break; 
     } 
     T previous = iterator.Current; 
     while (iterator.MoveNext()) 
     { 
      yield return previous; 
      previous = iterator.Current; 
     } 
    } 
} 

こと(シーケンスが空であれば早期に終了することによって)かなり深くネストすることを避け、「実」while条件を代わりに使用するwhile(true)

+0

マルコフの停止ポイントとは何ですか?とにかく、良い提案!深いネストと真実は、私のソリューションがちょっと変わった理由の一部でした。 – Svish

+0

@Jon、Last-family関数の実際の問題点を明確にしてくれてありがとう。私の場合、SkipLast(5)は5つの要素の後ろにある必要があるようです...おそらくQueueのための良い仕事のように見えます。 –

1

あなたの実装は私にとってはまったく上手く見えます - それはおそらく私がやる方法です。

私があなたの状況に関連して示唆している唯一の単純化は、リストの逆順()を(降順ではなく昇順で)並べることです。これはあなたのコードには適していないかもしれませんが、単に最新のものを除くすべての項目を取るためにcollection.Skip(1)を使うことができます。

投稿に表示されていない理由でこれができない場合は、現在の実装にはまったく問題ありません。

+0


SECOND
THIRDはい。実際にはそれについては考えていませんでしたが、後で順序付けが必要です。それは私がそれを注文してから、それをもう一度か何かを逆にしなければならないことを意味します。あまりにも私が推測することができたかもしれない...しかし、そのように前後する少し奇妙な感じ:p – Svish

0

は、.NET 3.5を使用している場合、私はあなたが使用できると思います:

廃棄
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source) 
{ 
    return source.TakeWhile((item, index) => index < source.Count() - 1)) 
} 
+0

これはコレクションを2回列挙しますが(実際にはsourcisがリストでない限り)、それはおそらく悪い考えです! (また、非決定論的イテレータの一般的なケースでも同じことはしません) – Noldorin

+1

Countの代わりにCount()も必要になります。そして、実際には、ラムダ式が評価されるたびにノードを数えるので、コレクションを何度も何度も列挙します! –

+0

私自身の教育のために:カウントがラムダの外で一度行われたらそれは大丈夫でしょうか?なぜコレクションが2回列挙されるのかは分かりません。 – Razzie

0
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source) 
{ 
    if (!source.Any()) 
    { 
     yield break; 
    } 
    Queue<T> items = new Queue<T>(); 
    items.Enqueue(source.First()); 
    foreach(T item in source.Skip(1)) 
    { 
     yield return items.Dequeue(); 
     items.Enqueue(item); 
    } 
} 
0

(旧答え。このコードはテスト済みで、動作しています。)これは、印刷


FIRST良い点である


public static class ExtNum{ 
    public static IEnumerable skipLast(this IEnumerable source){ 
    if (! source.Any()) 
     yield break; 
    for (int i = 0 ; i <=source.Count()-2 ; i++) 
     yield return source.ElementAt(i); 
    yield break; 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
    Queue qq = new Queue(); 
    qq.Enqueue("first");qq.Enqueue("second");qq.Enqueue("third"); 
    List lq = new List(); 
    lq.Add("FIRST"); lq.Add("SECOND"); lq.Add("THIRD"); lq.Add("FOURTH"); 
    foreach(string s1 in qq.skipLast()) 
     Console.WriteLine(s1); 
    foreach (string s2 in lq.skipLast()) 
     Console.WriteLine(s2); 
    } 
} 
+0

IEnumerableにCountまたはインデクサーがありません。だから、これを行うにはリストや何かに入れなければなりません。そうすれば、あなたは要素を2回通らなければならなくなります。一度それらをリストに入れ、一度それらを得る。 – Svish