Ser*_*kiy 94
您不需要编写任何代码.使用MoreLINQ Batch方法,将源序列批量化为大小的存储桶(MoreLINQ可用作您可以安装的NuGet包):
int size = 10;
var batches = sequence.Batch(size);
Run Code Online (Sandbox Code Playgroud)
实现方式如下:
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IEnumerable<TSource> source, int size)
{
TSource[] bucket = null;
var count = 0;
foreach (var item in source)
{
if (bucket == null)
bucket = new TSource[size];
bucket[count++] = item;
if (count != size)
continue;
yield return bucket;
bucket = null;
count = 0;
}
if (bucket != null && count > 0)
yield return bucket.Take(count).ToArray();
}
Run Code Online (Sandbox Code Playgroud)
L.B*_*L.B 77
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
int maxItems)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / maxItems)
.Select(g => g.Select(x => x.item));
}
}
Run Code Online (Sandbox Code Playgroud)
用法是:
List<int> list = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach(var batch in list.Batch(3))
{
Console.WriteLine(String.Join(",",batch));
}
Run Code Online (Sandbox Code Playgroud)
OUTPUT:
0,1,2
3,4,5
6,7,8
9
Run Code Online (Sandbox Code Playgroud)
Nic*_*ley 27
以上所有都是大批量或低内存空间的执行.不得不写我自己的管道(注意没有任何项目积累):
public static class BatchLinq {
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size) {
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (IEnumerator<T> enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
yield return TakeIEnumerator(enumerator, size);
}
private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size) {
int i = 0;
do
yield return source.Current;
while (++i < size && source.MoveNext());
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:此方法的已知问题是,在移动到下一批之前,必须完全枚举和枚举每个批次.例如,这不起作用:
//Select first item of every 100 items
Batch(list, 100).Select(b => b.First())
Run Code Online (Sandbox Code Playgroud)
Mat*_*dge 26
如果你从sequence定义为a 开始IEnumerable<T>,并且你知道它可以安全地多次枚举(例如,因为它是一个数组或一个列表),你可以使用这个简单的模式来批量处理元素:
while (sequence.Any())
{
var batch = sequence.Take(10);
sequence = sequence.Skip(10);
// do whatever you need to do with each batch here
}
Run Code Online (Sandbox Code Playgroud)
inf*_*lch 15
这是Batch的完全惰性,低开销,单功能实现,不进行任何累积.在EricRoller的帮助下,基于(并修复了)Nick Whaley的解决方案.
迭代直接来自底层IEnumerable,因此元素必须按严格顺序枚举,并且访问不超过一次.如果内部循环中没有使用某些元素,则会丢弃它们(并尝试通过保存的迭代器再次访问它们InvalidOperationException: Enumeration already finished.).
您可以在.NET Fiddle上测试完整的示例.
public static class BatchLinq
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (var enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
{
int i = 0;
// Batch is a local function closing over `i` and `enumerator` that
// executes the inner batch enumeration
IEnumerable<T> Batch()
{
do yield return enumerator.Current;
while (++i < size && enumerator.MoveNext());
}
yield return Batch();
while (++i < size && enumerator.MoveNext()); // discard skipped items
}
}
}
Run Code Online (Sandbox Code Playgroud)
Mon*_*Zhu 13
我想知道为什么没有人发布过老式的 for 循环解决方案。这是一个:
List<int> 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);
}
Run Code Online (Sandbox Code Playgroud)
这种简单性是可能的,因为 Take 方法:
... 枚举
source并产生元素,直到count元素被产生或不source包含更多元素。如果count超过 中的元素数source,source则返回 中的所有元素
免责声明:
在循环内使用 Skip 和 Take 意味着可枚举将被枚举多次。如果可枚举被延迟,这是危险的。它可能会导致多次执行数据库查询、Web 请求或文件读取。此示例明确用于未延迟的 List 的使用,因此问题不大。这仍然是一个缓慢的解决方案,因为每次调用时都会枚举集合。
这也可以使用该GetRange方法解决,但需要额外的计算来提取可能的剩余批次:
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);
}
Run Code Online (Sandbox Code Playgroud)
这是处理此问题的第三种方法,它适用于 2 个循环。这确保了该集合仅被枚举 1 次!:
int batchsize = 10;
List<int> batch = new List<int>(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();
}
Run Code Online (Sandbox Code Playgroud)
的Enumerable.Chunk()延伸方法是目前在预览和被提名被添加到.NET 6.0。
例子:
var list = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
var chunks = list.Chunk(3);
// returns { { 1, 2, 3 }, { 4, 5, 6 }, { 7 } }
Run Code Online (Sandbox Code Playgroud)
对于那些迫不及待的人,可以在 GitHub 上找到源代码。