Это то же самое, что принятый ответ, но гораздо более простое представление:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items,
int numOfParts)
{
int i = 0;
return items.GroupBy(x => i++ % numOfParts);
}
Приведенный выше метод разбивает IEnumerable<T>
на N кусков одинакового размера или близких к равным размерам.
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items,
int partitionSize)
{
int i = 0;
return items.GroupBy(x => i++ / partitionSize).ToArray();
}
Приведенный выше метод разбивает IEnumerable<T>
на куски желаемого фиксированного размера, причем общее количество кусков не имеет значения - вопрос не в этом.
Проблема с методом Split
, помимо того, что он медленнее, заключается в том, что он скремблирует выходные данные в том смысле, что группировка будет выполняться на основе i-го кратного N для каждой позиции или другими словами не получайте куски в первоначальном порядке.
Почти каждый ответ здесь либо не сохраняет порядок, либо касается разбиения, а не разбиения, либо является явно неправильным. Попробуйте это, что быстрее, сохраняет порядок, но немного более многословно:
public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items,
int numberOfChunks)
{
if (numberOfChunks <= 0 || numberOfChunks > items.Count)
throw new ArgumentOutOfRangeException("numberOfChunks");
int sizePerPacket = items.Count / numberOfChunks;
int extra = items.Count % numberOfChunks;
for (int i = 0; i < numberOfChunks - extra; i++)
yield return items.Skip(i * sizePerPacket).Take(sizePerPacket);
int alreadyReturnedCount = (numberOfChunks - extra) * sizePerPacket;
int toReturnCount = extra == 0 ? 0 : (items.Count - numberOfChunks) / extra + 1;
for (int i = 0; i < extra; i++)
yield return items.Skip(alreadyReturnedCount + i * toReturnCount).Take(toReturnCount);
}
Эквивалентный метод для операции Partition
здесь