How to take all but the last element in a sequence using LINQ?

前端 未结 22 1535
南笙
南笙 2020-11-30 02:51

Let\'s say I have a sequence.

IEnumerable sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000
         


        
22条回答
  •  粉色の甜心
    2020-11-30 03:14

    Because I'm not a fan of explicitly using an Enumerator, here's an alternative. Note that the wrapper methods are needed to let invalid arguments throw early, rather than deferring the checks until the sequence is actually enumerated.

    public static IEnumerable DropLast(this IEnumerable source)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        return InternalDropLast(source);
    }
    
    private static IEnumerable InternalDropLast(IEnumerable source)
    {
        T buffer = default(T);
        bool buffered = false;
    
        foreach (T x in source)
        {
            if (buffered)
                yield return buffer;
    
            buffer = x;
            buffered = true;
        }
    }
    

    As per Eric Lippert's suggestion, it easily generalizes to n items:

    public static IEnumerable DropLast(this IEnumerable source, int n)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        if (n < 0)
            throw new ArgumentOutOfRangeException("n", 
                "Argument n should be non-negative.");
    
        return InternalDropLast(source, n);
    }
    
    private static IEnumerable InternalDropLast(IEnumerable source, int n)
    {
        Queue buffer = new Queue(n + 1);
    
        foreach (T x in source)
        {
            buffer.Enqueue(x);
    
            if (buffer.Count == n + 1)
                yield return buffer.Dequeue();
        }
    }
    

    Where I now buffer before yielding instead of after yielding, so that the n == 0 case does not need special handling.

提交回复
热议问题