Can someone suggest a way to create batches of a certain size in linq?
Ideally I want to be able to perform operations in chunks of some configurable amount.
I wonder why nobody has ever posted an old school for-loop solution. Here is one:
List source = Enumerable.Range(1,23).ToList();
int batchsize = 10;
for (int i = 0; i < source.Count; i+= batchsize)
{
var batch = source.Skip(i).Take(batchsize);
}
This simplicity is possible because the Take method:
... enumerates
source
and yields elements untilcount
elements have been yielded orsource
contains no more elements. Ifcount
exceeds the number of elements insource
, all elements ofsource
are returned
Disclaimer:
Using Skip and Take inside the loop means that the enumerable will be enumerated multiple times. This is dangerous if the enumerable is deferred. It may result in multiple executions of a database query, or a web request, or a file read. This example is explicitly for the usage of a List which is not deferred, so it is less of a problem. It is still a slow solution since skip will enumerate the collection each time it is called.
This can also be solved using the GetRange
method, but it requires an extra calculation to extract a possible rest batch:
for (int i = 0; i < source.Count; i += batchsize)
{
int remaining = source.Count - i;
var batch = remaining > batchsize ? source.GetRange(i, batchsize) : source.GetRange(i, remaining);
}
Here is a third way to handle this, which works with 2 loops. This ensures that the collection is enumerated only 1 time!:
int batchsize = 10;
List batch = new List(batchsize);
for (int i = 0; i < source.Count; i += batchsize)
{
// calculated the remaining items to avoid an OutOfRangeException
batchsize = source.Count - i > batchsize ? batchsize : source.Count - i;
for (int j = i; j < i + batchsize; j++)
{
batch.Add(source[j]);
}
batch.Clear();
}