咨询区
BlakeH:
请问是否可以用 linq 按序生成带有多个固定 size 的块?我的理想情况下还可以对这些 块
进行操作。
回答区
Sergey Berezovskiy:
说实话,你不需要写任何代码,使用 MoreLINQ
中的批次方法即可,它可以按序拆解到固定大小的桶中,你可以直接在 Nuget 上安装 MoreLINQ 包。
int size = 10;
var batches = sequence.Batch(size);
如果你好奇它的实现方法,可参考如下源码:
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();
}
dana:
这个需求确实比较常见,在 .NET6 中添加了一个 Enumerable.Chunk()
扩展方法,然后你就可以像下面这样使用。
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 } }
如果你对源码感兴趣,可以参考如下:
public static IEnumerable<TSource[]> Chunk<TSource>(this IEnumerable<TSource> source, int size)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (size < 1)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.size);
}
return ChunkIterator(source, size);
}
private static IEnumerable<TSource[]> ChunkIterator<TSource>(IEnumerable<TSource> source, int size)
{
using IEnumerator<TSource> e = source.GetEnumerator();
while (e.MoveNext())
{
TSource[] array = new TSource[size];
array[0] = e.Current;
int i;
for (i = 1; i < array.Length; i++)
{
if (!e.MoveNext())
{
break;
}
array[i] = e.Current;
}
if (i == array.Length)
{
yield return array;
continue;
}
Array.Resize(ref array, i);
yield return array;
break;
}
}
点评区
发现 .NET6 下的 Enumerable 增加了一些 By 系列方法:MinBy
,ExceptBy
,IntersectBy
,又有得学了。