Skip to content

Instantly share code, notes, and snippets.

@rob-blackbourn
Last active September 7, 2022 16:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rob-blackbourn/e573ad0de40fa8e8c167 to your computer and use it in GitHub Desktop.
Save rob-blackbourn/e573ad0de40fa8e8c167 to your computer and use it in GitHub Desktop.
A C# linq extension to buffer an enumerable into an enumerable of enumerable blocks

Bufering in linq

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

Use Case

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.

Implementation

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());
    }
}

Test

[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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment