tsunami

history

C# 3.0: SelfJoinByOffset

Luke Breuer
2012-10-08 09:39 UTC

Uses Tuple<T1, T2>.
```[Flags]
public enum IncludeFringe
{
None = 0,
Begin = 1,
End = 2,
BeginAndEnd = Begin | End
}

/// <summary>
/// Joins a sequence to itself, offset by some number of elements.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="offset"></param>
/// <returns>
/// A Tuple with the second element being <code>offset</code> elements past the first.
/// The second element will be null for the last <code>offset</code> element(s) of the list.
/// </returns>
public static IEnumerable<Tuple<T, T>> SelfJoinByOffset<T>(this IEnumerable<T> list, int offset, IncludeFringe fringes)
{
if (offset <= 0)
throw new ArgumentOutOfRangeException("offset", "Must be greater than zero.");

/* Let's say the offset is 3, and we have 5 elements total
*
* A B C D E
*
* Then we will always have 3 elements in the Queue.  Here's the Q before we start yield returning:
*
* A B C D E
* ^ ^ ^       enqueued
*
* Now we yield return [(A, D), (B, E), (C, null), (D, null), (E, null)]
*
* What we're really doing is:
*   yield return (Q.Dequeue, e.Current);
*/

bool includeBegin = (fringes & IncludeFringe.Begin) != 0;
bool includeEnd = (fringes & IncludeFringe.End) != 0;

using (IEnumerator<T> e = list.GetEnumerator())
{
var q = new Queue<T>(offset);
bool hasMore = true;

while (offset-- > 0 && hasMore)
{
if (hasMore = e.MoveNext())
{
q.Enqueue(e.Current);

if (includeBegin)
yield return new Tuple<T, T>(default(T), e.Current);
}
}

while (q.Count > 0)
{
if (hasMore = e.MoveNext())
q.Enqueue(e.Current);

if (!hasMore && !includeEnd)
yield break;

yield return new Tuple<T, T>(q.Dequeue(), hasMore ? e.Current : default(T));
}
}
}
```
The input (with `offset` 1 and `IncludeFringe.End`)
```1, 2, 3, 4
```
would get returned as 4 `Tuple<int, int>`s:
```Tuple<int, int>.First:  1, 2, 3, 4
Tuple<int, int>.Second: 2, 3, 4, 0 <
^ this is default(int)
```