static void Main()
{
foreach (var block in "The quick brown fox jumped over the lazy dog".Split(' ').Buffer(4))
Console.WriteLine(string.Join(" ", block));
}
The code takes the sentence "The quick brown fox jumped over the lazy dog", splits it into words, buffers it into blocks of four words, then writes each block of words joined by spaces.
This outputs the following:
The quick brown fox
jumped over the lazy
dog
I was collecting the output of a program and forwarding it to a service. When the output got too large the service rejected the input. I needed a simple way to break the message into blocks, however as there was a lot of data I wanted to avoid creating multiple copies.
We can solve this without creating any temporary data structures.
public static class Extensions
{
public static IEnumerable<IEnumerable<T>> Buffer<T>(this IEnumerable<T> source, int count)
{
return Buffer(source.GetEnumerator(), count);
}
private static IEnumerable<IEnumerable<T>> Buffer<T>(IEnumerator<T> e, int count)
{
while (e.MoveNext())
yield return Next(e, count);
}
private static IEnumerable<T> Next<T>(IEnumerator<T> e, int count)
{
do yield return e.Current;
while (--count > 0 && e.MoveNext());
}
}
[TestCase(14, 5)]
[TestCase(7, 5)]
[TestCase(2, 2)]
[TestCase(1, 2)]
[TestCase(99, 10)]
public void TestBuffer(int n, int m)
{
int items = 0, blocks = 0;
foreach (var block in Enumerable.Range(0, n).Buffer(m))
{
++blocks;
items += block.Count();
}
Assert.AreEqual(n, items);
var expectedBlocks = n / m + (n % m == 0 ? 0 : 1);
Assert.AreEqual(expectedBlocks, blocks);
}