C #: implementazione SkipLast
-
13-09-2019 - |
Domanda
ho bisogno di un metodo per darmi tutti, ma l'ultimo elemento in una sequenza. Questo è il mio attuale implementazione:
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if(iterator.MoveNext())
while(true)
{
var current = iterator.Current;
if(!iterator.MoveNext())
yield break;
yield return current;
}
}
}
Che cosa ho bisogno per è di fare qualcosa con tutti gli elementi ad eccezione dell'ultimo. Nel mio caso ho una sequenza di oggetti con diverse proprietà. Ho poi ordinarli per data, e quindi ho bisogno di fare una rettifica a tutti loro, tranne la voce più recente (che sarebbe l'ultimo dopo l'ordinazione).
La cosa è, io non sono troppo in queste enumeratori e cose ancora e in realtà non hanno nessuno qui per chiedere uno: p Quello che mi chiedo è se questa è una buona implementazione, o se ho fatto un piccolo o grande sbagliare qualche parte. O se forse questo assumere il problema è uno strano, ecc.
Credo che un'implementazione più generale avrebbe potuto essere un metodo AllExceptMaxBy
. Dal momento che è una specie di cosa si tratta. Il MoreLinq ha un MaxBy
e il metodo MinBy
e il mio metodo tipo di esigenza di fare lo stesso, ma tornare ogni elemento ad eccezione di quella massima o minima.
Soluzione
Questo è difficile, come "ultimo elemento" non è un punto di arresto Markov: non si può dire che hai avuto modo di l'ultimo elemento fino a quando si tenta di ottenere il prossimo. E 'fattibile, ma solo se non vi dispiace essere in modo permanente "un elemento dietro". Questo è fondamentalmente ciò che l'implementazione corrente lo fa, e sembra a posto, anche se probabilmente sarei scrivo in modo leggermente diverso.
Un approccio alternativo sarebbe quella di utilizzare foreach
, sempre ottenendo il valore precedentemente restituito a meno che non si erano alla prima iterazione:
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;
}
}
Un'altra opzione, più vicino al codice:
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;
}
}
}
Questo evita la nidificazione altrettanto profondamente (facendo una rapida uscita se la sequenza è vuota) e si utilizza un "vero", mentre condizioni invece di while(true)
Altri suggerimenti
L'implementazione sembra perfettamente bene per me -. È probabilmente il modo in cui lo farei
L'unica semplificazione Potrei suggerire in relazione alla vostra situazione è di ordinare la lista il contrario (vale a dire ascendente invece che discendente). Anche se questo potrebbe non essere adatto nel codice, sarebbe consentono di utilizzare semplicemente collection.Skip(1)
a prendere tutte le voci tranne la più recente.
Se questo non è possibile per ragioni che non hanno mostrato nel tuo post, quindi l'implementazione corrente è affatto un problema.
Se si sta utilizzando .NET 3.5, penso che si potrebbe usare:
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
return source.TakeWhile((item, index) => index < source.Count() - 1))
}
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);
}
}
(Old risposta scartato;. Questo codice è stato testato e funziona) Esso stampa
prima
secondo
PRIMO
SECONDO
TERZO
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);
}
}