Мы получили это в MoreLINQ как Batch
.
var batch = source.Batch(3);
Как видно из кода, реализовать его на самом деле нетривиальноэффективно со «стандартными» операторами LINQ, но это явно выполнимо.Обратите внимание, что это включает в себя буферизацию ввода, так как результирующие последовательности должны быть независимыми.
Если вы do хотите сделать это только со стандартными операторами, менее эффективная реализация будет:
// Assume "size" is the batch size
var query = source.Select((value, index) => new { value, index })
.GroupBy(pair => pair.index / size, pair => pair.value);
РЕДАКТИРОВАТЬ: просто чтобы показать, почему это безопаснее, чем ответ Джона Фишера, вот короткая, но полная программа, чтобы показать разницу:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main(String[] args)
{
int[] source = { 1, 2, 3, 4, 5, 6, 7 };
var skeet = SkeetAnswer(source, 3);
var fisher = FisherAnswer(source, 3);
Console.WriteLine("Size of the first element of Skeet's solution:");
Console.WriteLine(skeet.First().Count());
Console.WriteLine(skeet.First().Count());
Console.WriteLine(skeet.First().Count());
Console.WriteLine("Size of the first element of Fisher's solution:");
Console.WriteLine(fisher.First().Count());
Console.WriteLine(fisher.First().Count());
Console.WriteLine(fisher.First().Count());
}
static IEnumerable<IEnumerable<int>> SkeetAnswer(IEnumerable<int> source,
int size)
{
return source.Select((value, index) => new { value, index })
.GroupBy(pair => pair.index / size, pair => pair.value);
}
static IEnumerable<IEnumerable<int>> FisherAnswer(IEnumerable<int> source,
int size)
{
int index = 0;
return source.GroupBy(x => (index++ / size));
}
}
Результаты:
Size of the first element of Skeet's solution:
3
3
3
Size of the first element of Fisher's solution:
3
2
1
В то время как вы могли бы позвонить ToList()
в конце, в этот момент вы потеряли повышение эффективности подхода - в основном подход Джона избегает создания экземпляра анонимного типа для каждого члена.Этого можно избежать, используя тип значения, эквивалентный Tuple<,>
, чтобы не создавалось больше объектов , только пары значений, заключенных в другое значение.Все еще будет небольшое увеличение времени, необходимого для выполнения проекции, а затем группировки.
Это хорошая демонстрация того, почему возникают побочные эффекты в ваших запросах LINQ (в этом случае модификация захваченной переменной index
) - плохая идея.
Другой альтернативой было бы написать реализацию GroupBy
, которая обеспечивает index
каждого элемента для проекции ключа.Это то, что так хорошо в LINQ - есть так много вариантов!