SkipLast of an IEnumerable - Linq Extension As my answer to this question, I came up with this solution:

static public IEnumerable&lt;T&gt; SkipLast&lt;T&gt;(this IEnumerable&lt;T&gt; data, int count)
{
  if (data == null || count &lt; 0) yield break;

  Queue&lt;T&gt; queue = new Queue&lt;T&gt;(data.Take(count));

  foreach (T item in data.Skip(count))
  {
    queue.Enqueue(item);
    yield return queue.Dequeue();
  }
}


It returns all items in the set but the count last, but without knowing anything about the size of the data set. I think it's funny and that it's working, but a comment claims that is doesn't. Am I overlooking something?



A version with a circular queue could be:

static public IEnumerable&lt;T&gt; SkipLast&lt;T&gt;(this IEnumerable&lt;T&gt; data, int count)
{
  if (data == null || count &lt; 0) yield break;
  if (count == 0)
  {
    foreach (T item in data)
      yield return item;
  }
  else
  {
    T[] queue = data.Take(count).ToArray();
    int index = 0;

    foreach (T item in data.Skip(count))
    {
      index %= count;
      yield return queue[index];
      queue[index] = item;
      index++;
    }
  }
}


Performance wise they seems to be even.



Compared to other solutions like the most obvious:

data.Reverse().Skip(count).Reverse()


It seems to be at least as fast and for very large set about twice as fast.

Test case:

  int count = 20;
  var data = Enumerable.Range(1, count);

  for (int i = 0; i &lt; count + 5; i++)
  {
    Console.WriteLine($"Skip: {i} =&gt; {(string.Join(", ", data.SkipLast1(i)))}");
  }


Any comments are useful.
There are 2 cases for which both your and VisualMelon's implementation can be improved:


If count is less than or equal to 0, then you can return source directly. This speeds up iteration by removing an unnecessary intermediate step. For this to work, the yielding part of the method has to be moved into another method, but that's easy with local functions.
If source is ICollection&lt;T&gt; collection, then you can return source.Take(collection.Count - count), which is faster and uses less memory because it doesn't need to buffer anything. The higher count is, the more of a difference this makes.


The first of these is probably not a very useful edge-case, so it might not be worth the extra code, but I would definitely include the second optimization.