咨询区
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
,又有得学了。