Let's say I have a sequence.
IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000
Getting the sequence is not cheap and is dynamically generated, and I want to iterate through it once only.
I want to get 0 - 999999 (i.e. everything but the last element)
I recognize that I could do something like:
sequence.Take(sequence.Count() - 1);
but that results in two enumerations over the big sequence.
Is there a LINQ construct that lets me do:
sequence.TakeAllButTheLastElement();
My traditional
IEnumerable
approach:This is a general and IMHO elegant solution that will handle all cases correctly:
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.As per Eric Lippert's suggestion, it easily generalizes to n items:
Where I now buffer before yielding instead of after yielding, so that the
n == 0
case does not need special handling.With C# 8.0 you can use Ranges and indices for that.
By default C# 8.0 requires .NET Core 3.0 or .NET Standard 2.1 (or above). Check this thread to use with older implementations.
The
Enumerable.SkipLast(IEnumerable<TSource>, Int32)
method was added in .NET Standard 2.1. It does exactly what you want.From https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast
You could write: