Create batches in linq

前端 未结 16 2142
傲寒
傲寒 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条回答
  •  闹比i
    闹比i (楼主)
    2020-11-22 03:50

    Here is an attempted improvement of Nick Whaley's (link) and infogulch's (link) lazy Batch implementations. This one is strict. You either enumerate the batches in the correct order, or you get an exception.

    public static IEnumerable> Batch(
        this IEnumerable source, int size)
    {
        if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
        using (var enumerator = source.GetEnumerator())
        {
            int i = 0;
            while (enumerator.MoveNext())
            {
                if (i % size != 0) throw new InvalidOperationException(
                    "The enumeration is out of order.");
                i++;
                yield return GetBatch();
            }
            IEnumerable GetBatch()
            {
                while (true)
                {
                    yield return enumerator.Current;
                    if (i % size == 0 || !enumerator.MoveNext()) break;
                    i++;
                }
            }
        }
    }
    

    And here is a lazy Batch implementation for sources of type IList. This one imposes no restrictions on the enumeration. The batches can be enumerated partially, in any order, and more than once. The restriction of not modifying the collection during the enumeration is still in place though. This is achieved by making a dummy call to enumerator.MoveNext() before yielding any chunk or element. The downside is that the enumerator is left undisposed, since it is unknown when the enumeration is going to finish.

    public static IEnumerable> Batch(
        this IList source, int size)
    {
        if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
        var enumerator = source.GetEnumerator();
        for (int i = 0; i < source.Count; i += size)
        {
            enumerator.MoveNext();
            yield return GetChunk(i, Math.Min(i + size, source.Count));
        }
        IEnumerable GetChunk(int from, int toExclusive)
        {
            for (int j = from; j < toExclusive; j++)
            {
                enumerator.MoveNext();
                yield return source[j];
            }
        }
    }
    

提交回复
热议问题