-
Notifications
You must be signed in to change notification settings - Fork 415
/
Batch.cs
156 lines (143 loc) · 6.78 KB
/
Batch.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#region License and Terms
// MoreLINQ - Extensions to LINQ to Objects
// Copyright (c) 2009 Atif Aziz. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
namespace MoreLinq
{
using System;
using System.Collections.Generic;
static partial class MoreEnumerable
{
/// <summary>
/// Batches the source sequence into sized buckets.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="size">Size of buckets.</param>
/// <returns>A sequence of equally sized buckets containing elements of the source collection.</returns>
/// <remarks>
/// <para>
/// This operator uses deferred execution and streams its results
/// (buckets are streamed but their content buffered).</para>
/// <para>
/// When more than one bucket is streamed, all buckets except the last
/// is guaranteed to have <paramref name="size"/> elements. The last
/// bucket may be smaller depending on the remaining elements in the
/// <paramref name="source"/> sequence.</para>
/// <para>
/// Each bucket is pre-allocated to <paramref name="size"/> elements.
/// If <paramref name="size"/> is set to a very large value, e.g.
/// <see cref="int.MaxValue"/> to effectively disable batching by just
/// hoping for a single bucket, then it can lead to memory exhaustion
/// (<see cref="OutOfMemoryException"/>).
/// </para>
/// </remarks>
public static IEnumerable<TSource[]> Batch<TSource>(this IEnumerable<TSource> source, int size)
{
return Batch(source, size, IdFn);
}
/// <summary>
/// Batches the source sequence into sized buckets and applies a projection to each bucket.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <typeparam name="TResult">Type of result returned by <paramref name="resultSelector"/>.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="size">Size of buckets.</param>
/// <param name="resultSelector">The projection to apply to each bucket.</param>
/// <returns>A sequence of projections on equally sized buckets containing elements of the source collection.</returns>
/// <remarks>
/// <para>
/// This operator uses deferred execution and streams its results
/// (buckets are streamed but their content buffered).</para>
/// <para>
/// <para>
/// When more than one bucket is streamed, all buckets except the last
/// is guaranteed to have <paramref name="size"/> elements. The last
/// bucket may be smaller depending on the remaining elements in the
/// <paramref name="source"/> sequence.</para>
/// Each bucket is pre-allocated to <paramref name="size"/> elements.
/// If <paramref name="size"/> is set to a very large value, e.g.
/// <see cref="int.MaxValue"/> to effectively disable batching by just
/// hoping for a single bucket, then it can lead to memory exhaustion
/// (<see cref="OutOfMemoryException"/>).
/// </para>
/// </remarks>
public static IEnumerable<TResult> Batch<TSource, TResult>(this IEnumerable<TSource> source, int size,
Func<TSource[], TResult> resultSelector)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
return _(source, size, resultSelector);
static IEnumerable<TResult> _(IEnumerable<TSource> source, int size, Func<TSource[], TResult> resultSelector)
{
switch (source)
{
case ICollection<TSource> { Count: 0 }:
{
break;
}
case ICollection<TSource> collection when collection.Count <= size:
{
var bucket = new TSource[collection.Count];
collection.CopyTo(bucket, 0);
yield return resultSelector(bucket);
break;
}
case IReadOnlyCollection<TSource> { Count: 0 }:
{
break;
}
case IReadOnlyList<TSource> list when list.Count <= size:
{
var bucket = new TSource[list.Count];
for (var i = 0; i < list.Count; i++)
bucket[i] = list[i];
yield return resultSelector(bucket);
break;
}
case IReadOnlyCollection<TSource> collection when collection.Count <= size:
{
size = collection.Count;
goto default;
}
default:
{
TSource[]? bucket = null;
var count = 0;
foreach (var item in source)
{
bucket ??= new TSource[size];
bucket[count++] = item;
// The bucket is fully buffered before it's yielded
if (count != size)
continue;
yield return resultSelector(bucket);
bucket = null;
count = 0;
}
// Return the last bucket with all remaining elements
if (count > 0)
{
Array.Resize(ref bucket, count);
yield return resultSelector(bucket);
}
break;
}
}
}
}
}
}