Вопрос

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

    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 способ.Поскольку это в некотором роде то, что есть.В МореЛинк имеет MaxBy и MinBy метод и мой метод вроде как должны делать то же самое, но возвращать все элементы, кроме максимального или минимального.

Это было полезно?

Решение

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

Альтернативный подход заключался бы в использовании 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)

Другие советы

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

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

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

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

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

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


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);
  }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top