Create batches in linq

前端 未结 16 2048
傲寒
傲寒 2020-11-22 02:50

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.

16条回答
  •  Happy的楠姐
    2020-11-22 03:28

    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 until count elements have been yielded or source contains no more elements. If count exceeds the number of elements in source, all elements of source 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();
    }
    

提交回复
热议问题