C #: implementação SkipLast
-
13-09-2019 - |
Pergunta
Eu precisava de um método para me todos, mas o último item dar em uma seqüência. Esta é a minha implementação atual:
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;
}
}
}
O que eu preciso dele para é fazer algo com todos os itens, exceto a última. No meu caso eu tenho uma seqüência de objetos com várias propriedades. Eu então encomendá-los por data, e, em seguida, eu preciso fazer um ajuste para todos eles, exceto o item mais recente (o que seria o último após a encomenda).
A coisa é, eu não sou muito para essas recenseadores e outras coisas ainda e realmente não tem ninguém aqui para pedir qualquer um: p O que eu estou querendo saber é se esta é uma boa implementação, ou se eu fiz uma pequena ou grande errar em algum lugar. Ou se talvez esta visão sobre o problema é uma estranha, etc.
Eu acho que uma aplicação mais geral poderia ter sido um método AllExceptMaxBy
. Desde que é uma espécie do que é. A MoreLINQ tem uma MaxBy
e método MinBy
e meu método de tipo de necessidade de fazer o mesmo, mas devolver todos os itens, exceto o máximo ou mínimo um.
Solução
Isto é complicado, como "último elemento" não é um ponto de parada Markov: você não pode dizer que você tem que o último elemento até que você tentar obter o próximo. É factível, mas apenas se você não se importa permanentemente ser "um elemento por trás". Isso é basicamente o que a sua implementação atual faz, e ele parece bem, embora eu provavelmente escrevê-lo de forma ligeiramente diferente.
Uma abordagem alternativa seria usar foreach
, sempre produzindo o valor previamente retornado a menos que você estava na primeira iteração:
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;
}
}
Outra opção, mais perto de seu código:
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;
}
}
}
Isso evita nidificação tão profundamente (fazendo uma saída precoce se a sequência estiver vazia) e ele usa um "real", enquanto condição em vez de while(true)
Outras dicas
sua implementação parece perfeitamente bem para mim. - É provavelmente a maneira que eu iria fazê-lo
A única simplificação eu poderia sugerir em relação à sua situação é para ordenar a lista o contrário (ou seja ascendente, em vez de descer). Embora isso possa não ser adequado em seu código, seria permitir que você simplesmente usar collection.Skip(1)
tomar todos os itens exceto o mais recente.
Se isto não for possível, por razões que não têm mostrado em seu post, então a sua implementação atual é nenhum problema em tudo.
Se você estiver usando o .NET 3.5, eu acho que você poderia usar:
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);
}
}
(resposta Old desfeito;. Esse código foi testado e funciona) Imprime
primeiro
segunda
PRIMEIRO
SEGUNDA
TERCEIRO
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);
}
}