hits counter

LINQ: Implementing The SkipLastWhile Operator

LINQ Com C#

Following my last posts (>)(>), in this post I’ll introduce the implementation of the SkipLastWhile operator.

The SkipLastWhile returns all but the last contiguous elements from a a sequence that satisfy the specified criteria and is implemented as the SkipLastWhile extension methods:

public static IEnumerable<TSource> SkipLastWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

public static IEnumerable<TSource> SkipLastWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)

The implementation of these methods is very simple. We start with an empty buffer and buffer every item that satisfies the criteria implemented by a predicate. Whenever an item doesn’t satisfy the criteria, all buffered items are yield, the buffer is cleared and the the item that doesn’t satisfy the criteria is yield:

var buffer = new List<TSource>();

foreach (var item in source)
{
    if (predicate(item))
    {
        buffer.Add(item);
    }
    else
    {
        if (buffer.Count > 0)
        {
            foreach (var bufferedItem in buffer)
            {
                yield return bufferedItem;
            }

            buffer.Clear();
        }

        yield return item;
    }
}

The overload that takes in account the index of the item only differs in the call the predicate that implements the criteria:

var buffer = new List<TSource>();
var idx = 0;

foreach (var item in source)
{
    if (predicate(item, idx++))
    {
        buffer.Add(item);
    }
    else
    {
        if (buffer.Count > 0)
        {
            foreach (var bufferedItem in buffer)
            {
                yield return bufferedItem;
            }

            buffer.Clear();
        }

        yield return item;
    }
}

You can find the complete implementation of this operator (and more) CodePlex project for LINQ utilities and operators: PauloMorgado.Linq

5 Comments

  • If I understand correctly, you reimplemented the TakeWhile() operator with a tweak that it also returns the first item that does not match. Out of sequence 123454321 using predicate x <= 3, your implementation returns 1234. However I would expect it to behave symmetrically to SkipWhile() operator and would expect return value to be 123454.

  • @Juozas, 123454 is exactly what it is supposed to return.
    Isn't that what's being returned?
    I just noticed that I had forgotten to check in the latest version, but I suppose the one I hade checked in previously worked.

  • @Paulo, I don't use your library, so I don't know whether actual code works. I'm just trying to point out that the code in this blog post does not seem to do what it's supposed to do.

  • @Juozas, how come I doesn’t seem to do?
    Let’s try it out:
    1 – the predicate is true, so it’s buffered
    2 – the predicate is true, so it’s buffered
    3 – the predicate is true, so it’s buffered
    4 – the predicate is false, so all buffered items are yield (1, 2, 3) and the current item (4)
    5 – the predicate is false, so all buffered items are yield () and the current item (5)
    4 – the predicate is false, so all buffered items are yield () and the current item (4)
    3 – the predicate is true, so it’s buffered
    2 – the predicate is true, so it’s buffered
    1 – the predicate is true, so it’s buffered
    The iterator ends without yield the buffered items.
    The output will be 1,2,3,4,5 and 4.
    Am I missing something?

  • @Paulo, I now see what I somehow managed to miss when I tried to analyze your code :)

    Anyway, thanks for the series. It was an interesting read.

Comments have been disabled for this content.