C #: реализация SkipLast - PullRequest
       17

C #: реализация SkipLast

1 голос
/ 09 июня 2009

Мне нужен был метод, чтобы дать мне все, кроме последнего элемента в последовательности. Это моя текущая реализация:

    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;
                }
        }
    }

Мне нужно что-то сделать со всеми предметами, кроме последнего. В моем случае у меня есть последовательность объектов с различными свойствами. Затем я упорядочиваю их по дате, а затем мне нужно выполнить настройку для всех из них, кроме самого последнего элемента (который будет последним после заказа).

Дело в том, что я не слишком разбираюсь в этих перечислителях и прочем, и на самом деле у меня нет никого, чтобы спросить: p Что мне интересно, так это хорошая реализация или небольшая или большая промах где-то. Или, может быть, это странная проблема и т. Д.

Полагаю, что более общей реализацией мог бы быть AllExceptMaxBy метод. Так как это отчасти то, что есть. У MoreLinq есть метод MaxBy и MinBy, и мой метод должен делать то же самое, но возвращать каждый элемент, кроме максимального или минимального.

Ответы [ 5 ]

9 голосов
/ 09 июня 2009

Это сложно, поскольку «последний элемент» не является точкой остановки Маркова: вы не можете сказать, что добрались до последнего элемента, пока не попробуете получить следующий. Это выполнимо, но только если вы не против постоянно быть «одним элементом позади». Это в основном то, что делает ваша текущая реализация, и выглядит хорошо, хотя я бы, наверное, написал это немного по-другому.

Альтернативным подходом было бы использовать 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(true)

используется "реальное" условие while
1 голос
/ 09 июня 2009

Если вы используете .NET 3.5, я думаю, вы могли бы использовать:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
  return source.TakeWhile((item, index) => index < source.Count() - 1))
}
1 голос
/ 09 июня 2009

Ваша реализация выглядит прекрасно для меня - вероятно, я так и сделал.

Единственное упрощение, которое я мог бы предложить в связи с вашей ситуацией, состоит в том, чтобы упорядочить список в обратном направлении (т.е. по возрастанию, а не по убыванию). Хотя это может не подходить в вашем коде, это позволит вам просто использовать collection.Skip(1), чтобы взять все элементы, кроме самого последнего.

Если это невозможно по причинам, которые вы не указали в своем сообщении, то ваша текущая реализация не представляет никаких проблем.

0 голосов
/ 09 июня 2009

(старый ответ отменен; этот код проверен и работает.) Он печатает
первый
второй
ПЕРВЫЙ
ВТОРАЯ
ТРЕТИЙ


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 голосов
/ 09 июня 2009
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);
    }
}
...