Today I got a new reaction to a post from 2008. In that post I explained how you can wrap an IEnumarable<T> so that for each item you can directly find the next and previous item. This comes in handy when you need to compare each item with the item directly berore or after it.
In his comment, John Gardner said that he liked this code, but het noted that it could be a problem that the implementation of WithContext() creates two enumerators on the source sequence. This was exactly the reason why I allready created a new version of this little utility soem while ago, but for some reason had failed to post an update. So here you go:
/// <summary>
/// Contains extension methods for adding context information to Enumerated Elements
/// </summary>
public static class ContextEnumerator
{
/// <summary>
/// Creates a sequence providing the previous and next Item with each element
/// </summary>
/// <param name="source">The input sequence</param>
/// <returns>An output sequence with information about the context of each element</returns>
static public IEnumerable<ElementWithContext<T>> WithContext<T>(this IEnumerable<T> source)
{
var enumerator = source.GetEnumerator();
// get the first element from the source
if (!enumerator.MoveNext())
yield break;
T previous = default(T);
T current = enumerator.Current;
int index = 0;
// look ahead one item, so the second item becomes the first 'Next' item
while (enumerator.MoveNext())
{
T next = enumerator.Current;
yield return new ElementWithContext<T>(previous, current, next,
index, source, false);
previous = current;
current = next;
index++;
}
// Now yield the last element which has no 'Next' item
yield return new ElementWithContext<T>(previous, current, default(T),
index, source, true);
}
}
/// <summary>
/// Represents an Enumerated Element with additional properties about its conetxt in the sequence
/// </summary>
public class ElementWithContext<T>
{
/// <summary>All elements</summary>
private IEnumerable<T> _all;
/// <summary>The element before the current element</summary>
public T Previous { get; private set; }
/// <summary>The current element</summary>
public T Current { get; private set; }
/// <summary>The element after the current element</summary>
public T Next { get; private set; }
/// <summary>The index of the current element</summary>
public int Index { get; private set; }
/// <summary>True if this is the first element</summary>
public bool IsFirst { get { return Index == 0; } }
/// <summary>True if this is the last element</summary>
public bool IsLast { get; private set; }
/// <summary>The elements before the current element</summary>
public IEnumerable<T> Preceding { get { return _all.Take(Index); } }
/// <summary>The elements after the current element</summary>
public IEnumerable<T> Following { get { return _all.Skip(Index + 1); } }
internal ElementWithContext(T previous, T current, T next,
int index, IEnumerable<T> all, bool isLast)
{
Current = current;
Previous = previous;
Next = next;
Index = index;
_all = all;
IsLast = isLast;
}
}