2

我需要一种方法来给我除了序列中的最后一项之外的所有内容。这是我目前的实现:

    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有一个and方法,我的方法需要做同样的事情,但返回除最大值或最小值之外的每个项目。MaxByMinBy

4

6 回答 6

12

这很棘手,因为“最后一个元素”不是马尔可夫停止点:在尝试获取下一个元素之前,您无法判断您已经到达最后一个元素。这是可行的,但前提是您不介意永久成为“落后的元素”。这基本上就是您当前的实现所做的,看起来还不错,尽管我可能会写得稍微不同。

另一种方法是使用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)

于 2009-06-09T09:43:00.687 回答
2

如果您使用的是 .NET 3.5,我想您可以使用:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
  return source.TakeWhile((item, index) => index < source.Count() - 1))
}
于 2009-06-09T09:48:30.927 回答
1

您的实现对我来说看起来非常好 - 这可能是我会这样做的方式。

对于您的情况,我可能建议的唯一简化是将列表顺序反过来(即升序而不是降序)。虽然这可能不适合您的代码,但它可以让您简单地使用collection.Skip(1)来获取除最新项目之外的所有项目。

如果由于您未在帖子中显示的原因而无法做到这一点,那么您当前的实现完全没有问题。

于 2009-06-09T09:42:08.710 回答
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);
    }
}
于 2009-06-09T17:30:25.420 回答
0

(旧答案已废弃;此代码已经过测试并且可以工作。)它会打印
first
second
FIRST
SECOND
THIRD


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);
  }
}
于 2009-06-09T19:23:40.770 回答
0

结合所有答案,并使用<LangVersion>latest<LangVersion>

.NET 小提琴

public static class EnumerableExtensions
{
    // Source is T[]

    public static IEnumerable<T> SkipLast<T>(this T[] source, int count) =>
        source.TakeWhile((item, index) => index < source.Length - count);

    public static IEnumerable<T> SkipLast<T>(this T[] source) => source.SkipLast(1);

    // Source is ICollection<T>

    public static IEnumerable<T> SkipLast<T>(this ICollection<T> source, int count) =>
        source.TakeWhile((item, index) => index < source.Count - count);

    public static IEnumerable<T> SkipLast<T>(this ICollection<T> source) => source.SkipLast(1);


    // Source is unknown or IEnumerable<T>

    public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
    {
        switch (source)
        {
            case T[] array:
                return SkipLast(array, count);
            case ICollection<T> collection:
                return SkipLast(collection, count);
            default:
                return skipLast();
        }

        IEnumerable<T> skipLast()
        {
            using IEnumerator<T> iterator = source.GetEnumerator();
            if (!iterator.MoveNext())
                yield break;
            Queue<T> items = new Queue<T>(count);
            items.Enqueue(iterator.Current);
            for (int i = 1; i < count && iterator.MoveNext(); i++)
                items.Enqueue(iterator.Current);
            while (iterator.MoveNext())
            {
                yield return items.Dequeue();
                items.Enqueue(iterator.Current);
            }
        }
    }

    public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
    {
        switch (source)
        {
            case T[] array:
                return SkipLast(array);
            case ICollection<T> collection:
                return SkipLast(collection);
            default:
                return skipLast();
        }

        IEnumerable<T> skipLast()
        {
            using IEnumerator<T> iterator = source.GetEnumerator();
            if (!iterator.MoveNext())
                yield break;
            T previous = iterator.Current;
            while (iterator.MoveNext())
            {
                yield return previous;
                previous = iterator.Current;
            }
        }
    }    
}
于 2022-01-11T21:47:15.783 回答