我需要一个优雅的方法,它接受一个可枚举对象,并获取可枚举对象的可枚举对象,其中每个可枚举对象包含相同数量的元素,但最后一个元素除外:
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(this IEnumerable<TValue> values, Int32 chunkSize)
{
// TODO: code that chunks
}
这就是我所尝试的:
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(this IEnumerable<TValue> values, Int32 chunkSize)
{
var count = values.Count();
var numberOfFullChunks = count / chunkSize;
var lastChunkSize = count % chunkSize;
for (var chunkIndex = 0; chunkSize < numberOfFullChunks; chunkSize++)
{
yield return values.Skip(chunkSize * chunkIndex).Take(chunkSize);
}
if (lastChunkSize > 0)
{
yield return values.Skip(chunkSize * count).Take(lastChunkSize);
}
}
UPDATE刚发现有一个关于拆分列表的类似主题Split List into Sublists with LINQ
6条答案
按热度按时间0yg35tkg1#
如果内存消耗不是问题,那么像这样?
否则,您可以使用
yield
关键字来发挥创意,如下所示:bq8i3lrv2#
〉= .Net 6
内置Enumerable.Chunk方法:
muk1a3rh3#
下面是使用
Take
和Skip
的扩展方法:(更新为使用
IList
而不是IEnumerable
)x9ybnkn64#
如果你没有.net 6,你可能会选择把the Chunk method from it补丁到你的项目中,你可能需要做的唯一的调整是与.net源代码使用的异常助手有关,因为你自己的项目可能不会有
ThrowHelper
。他们的代号:
可能更像是:
以下代码块已应用了这些调整;您可以创建一个名为Chunk.cs的新文件并将以下代码放入其中:
您应该验证将他们的MIT许可代码合并到您的项目中不会过度影响您自己的许可意图
hs1rzwqc5#
仅进行了一些快速测试,但这似乎有效:
deikduxw6#
正如其他答案已经指出的那样,从.NET 6开始,存在
Enumerable.Chunk
扩展方法。不幸的是(在我看来),该方法返回
IEnumerable<T[]>
,这削弱了一次处理一个IEnumerable<T>
元素所带来的节省内存的好处:在这个范例中,
AggregateSomehow
中chunk
的基础型别将会是HugeObject[100]
,表示必须同时将HugeObject
的100个执行严修载入内存,才能执行方法呼叫。在
Enumerable.Chunk
可用之前,我曾经编写自己的扩展名为Partition
,如下所示:这种方法考虑到三个因素:
1.原始
source
只枚举一次(Skip/Take
实现经常会遗漏)1.在一般的巢状/链接LINQ运算式中,内存中一次只能有一个元素(目前的实作会忽略这个元素)
1.当任何分区只处理了一部分,然后过早地被释放时,
PartitionerEnumerator.Dispose
方法确保底层枚举数仍然转发计数的剩余部分(嵌套循环方法经常会错过这一点:)如果所有的子序列都被完全枚举,例如通过在它们上调用
Count
或Sum
,这种方法将工作,但是对于部分枚举,例如在它们上调用First
,它将失败:我的实现可以满足上述所有要求,但仍有几个缺点,这些缺点与我的应用程序无关,但前两个缺点可能就是.NET团队选择“简单方法”并使用立即填充的数组的原因:
1.如果你先保存所有的
Partition
对象,然后循环处理它们,一次一个元素,原始IEnumerable
的顺序在分区中不被保留,也就是说,第一个分区不保证包含前三个元素。作为一个副作用,如果元素的数量没有均匀地划分到分区大小中,哪个分区比size
短是“随机的”。甚至不一定保证只有一个分区更短。1.在并行环境中使用它会遇到与(1)相同的问题,但TBH我甚至从来没有考虑过我的代码的线程安全性。
1.过早枚举中止的好处(如在子序列上调用
Any
或All
)将不会阻止创建当前枚举的Partion的其余元素(尽管对于Chunk
显然也是如此,其中所有元素在进入块时被创建)因此,简而言之,如果您不打算使用并行化或不依赖于有序处理,并且在使用.NET 6的
Chunk
时遇到内存问题,我的旧代码可能会对您有所帮助。