Некоторые связанные чтения:
В противном случае незначительное отклонение принятый ответ для работы с перечислимыми (для ленивых)- загрузка и обработка, если список большой / дорогой).Я хотел бы отметить, что материализация каждого чанка / сегмента (например, через .ToList
или .ToArray
, или просто перечисление каждого чанка) может иметь побочные эффекты - см. Тесты.
Методы
// so you're not repeatedly counting an enumerable
IEnumerable<IEnumerable<T>> Chunk<T>(IEnumerable<T> list, int totalSize, int chunkSize) {
int i = 0;
while(i < totalSize) {
yield return list.Skip(i).Take(chunkSize);
i += chunkSize;
}
}
// convenience for "countable" lists
IEnumerable<IEnumerable<T>> Chunk<T>(ICollection<T> list, int chunkSize) {
return Chunk(list, list.Count, chunkSize);
}
IEnumerable<IEnumerable<T>> Chunk<T>(IEnumerable<T> list, int chunkSize) {
return Chunk(list, list.Count(), chunkSize);
}
Тест (Linqpad)
(примечание: мне пришлось включить методы Assert
для linqpad)
void Main()
{
var length = 10;
var size = 4;
test(10, 4);
test(10, 6);
test(10, 2);
test(10, 1);
var sideeffects = Enumerable.Range(1, 10).Select(i => {
string.Format("Side effect on {0}", i).Dump();
return i;
});
"--------------".Dump("Before Chunking");
var result = Chunk(sideeffects, 4);
"--------------".Dump("After Chunking");
result.Dump("SideEffects");
var list = new List<int>();
foreach(var segment in result) {
list.AddRange(segment);
}
list.Dump("After crawling");
var segment3 = result.Last().ToList();
segment3.Dump("Last Segment");
}
// test
void test(int length, int size) {
var list = Enumerable.Range(1, length);
var c1 = Chunk(list, size);
c1.Dump(string.Format("Results for [{0} into {1}]", length, size));
Assert.AreEqual( (int) Math.Ceiling( (double)length / (double)size), c1.Count(), "Unexpected number of chunks");
Assert.IsTrue(c1.All(c => c.Count() <= size), "Unexpected size of chunks");
}