SkipLast & TakeLast
///
/// Enumerates the items of this collection, skipping the last
/// items. Note that the memory usage of this method
/// is proportional to , but the source collection is
/// only enumerated once, and in a lazy fashion. Also, enumerating the first
/// item will take longer than enumerating subsequent items.
///
public static IEnumerable SkipLast(this IEnumerable source, int count)
{
if (source == null)
throw new ArgumentNullException("source");
if (count < 0)
throw new ArgumentOutOfRangeException("count",
"count cannot be negative.");
if (count == 0)
return source;
return skipLastIterator(source, count);
}
private static IEnumerable skipLastIterator(IEnumerable source,
int count)
{
var queue = new T[count];
int headtail = 0; // tail while we're still collecting, both head & tail
// afterwards because the queue becomes completely full
int collected = 0;
foreach (var item in source)
{
if (collected < count)
{
queue[headtail] = item;
headtail++;
collected++;
}
else
{
if (headtail == count) headtail = 0;
yield return queue[headtail];
queue[headtail] = item;
headtail++;
}
}
}
///
/// Returns a collection containing only the last
/// items of the input collection. This method enumerates the entire
/// collection to the end once before returning. Note also that the memory
/// usage of this method is proportional to .
///
public static IEnumerable TakeLast(this IEnumerable source, int count)
{
if (source == null)
throw new ArgumentNullException("source");
if (count < 0)
throw new ArgumentOutOfRangeException("count",
"count cannot be negative.");
if (count == 0)
return new T[0];
var queue = new Queue(count + 1);
foreach (var item in source)
{
if (queue.Count == count)
queue.Dequeue();
queue.Enqueue(item);
}
return queue.AsEnumerable();
}