我有方法Process(IEnumerable<Record> records)
,它可以接受但一次不超过3条记录。我有数百条记录,所以我需要分组传递。我这样做:
var _Records = Enumerable.Range(1, 16).ToArray();
for (int i = 0; i < int.MaxValue; i += 3)
{
var _ShortList = _Records.Skip(i).Take(3);
if (!_ShortList.Any())
break;
Process(_ShortList);
}
// TODO: finish
它有效,但......有更好的方法吗?
答案 0 :(得分:4)
您可以使用MoreLinq的Batch
var result=Enumerable.Range(1, 16).Batch(3);
或
var arrayOfArrays = Enumerable.Range(1, 16).Batch(3).Select(x => x.ToArray()).ToArray();
如果你想看一下,这里是source。
答案 1 :(得分:1)
您可以使用此扩展方法:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkSize)
{
return source
.Select((value, i) => new { Index = i, Value = value })
.GroupBy(item => item.Index % chunkSize)
.Select(chunk => chunk.Select(item => item.Value));
}
它将项目的源集合拆分为具有给定大小的多个块。 所以你的代码将会是下一个:
foreach (var chunk in Enumerable.Range(1, 16).Split(3))
{
Process(chunk);
}
答案 2 :(得分:1)
这是另一种LINQ-y方式:
var batchSize = 3;
Enumerable.Range(0, (_Records.Length - 1)/batchSize + 1)
.ToList()
.ForEach(i => Process(_Records.Skip(i * batchSize).Take(batchSize)));
答案 3 :(得分:0)
如果您的解决方案需要多次“分页”,您可以考虑使用扩展方法。
在LINQPad中一起黑客攻击,它就可以解决问题。
public static class MyExtensions {
public static IEnumerable<IEnumerable<T>> Paginate<T>(this IEnumerable<T> source, int pageSize) {
T[] buffer = new T[pageSize];
int index = 0;
foreach (var item in source) {
buffer[index++] = item;
if (index >= pageSize) {
yield return buffer.Take(pageSize);
index = 0;
}
}
if (index > 0) {
yield return buffer.Take(index);
}
}
}
基本上,它预先填充大小为pageSize
的缓冲区,并在它满时生成它。如果剩下< pageSize
个元素,我们也会产生它们。所以,
Enumerable.Range(1, 10).Paginate(3).Dump(); // Dump is a LINQPad extension
将产生
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10}}
答案 4 :(得分:0)
var _Records = Enumerable.Range(1, 16).ToArray();
int index = 0;
foreach (var group in _Records.GroupBy(element => index++ / 3))
Process(group);
注意:上面的代码很短且相对有效,但仍然没有那么高效(它实际上将在幕后构建一个哈希表)。稍微麻烦但更快的方法是:
var _Records = Enumerable.Range(1, 16).ToArray();
var buff = new int[3];
int index = 0;
foreach (var element in _Records) {
if (index == buff.Length) {
Process(buff);
index = 0;
}
buff[index++] = element;
}
if (index > 0)
Process(buff.Take(index));
或者,将其打包成更可重复使用的形式:
public static class EnumerableEx {
public static void Paginate<T>(this IEnumerable<T> elements, int page_size, Action<IEnumerable<T>> process_page) {
var buff = new T[3];
int index = 0;
foreach (var element in elements) {
if (index == buff.Length) {
process_page(buff);
index = 0;
}
buff[index++] = element;
}
if (index > 0)
process_page(buff.Take(index));
}
}
// ...
var _Records = Enumerable.Range(1, 16).ToArray();
_Records.Paginate(3, Process);
答案 5 :(得分:0)
您可以创建自己的扩展方法:
static class Extensions {
public static IEnumerable<IEnumerable<T>> ToBlocks<T>(this IEnumerable<T> source, int blockSize) {
var count = 0;
T[] block = null;
foreach (var item in source) {
if (block == null)
block = new T[blockSize];
block[count++] = item;
if (count == blockSize) {
yield return block;
block = null;
count = 0;
}
}
if (count > 0)
yield return block.Take(count);
}
}
答案 6 :(得分:0)
public static void ChunkProcess<T>(IEnumerable<T> source, int size, Action<IEnumerable<T>> action)
{
var chunk = source.Take(size);
while (chunk.Any())
{
action(chunk);
source = source.Skip(size);
chunk = source.Take(size);
}
}
,您的代码将是
ChunkProcess(_Records, 3, Process);
答案 7 :(得分:0)
此扩展方法正常运行。
public static class EnumerableExtentions
{
public static IEnumerable<IEnumerable<T>> Chunks<T>(this IEnumerable<T> items, int size)
{
return
items.Select((member, index) => new { Index = index, Value = member })
.GroupBy(item => (int)item.Index / size)
.Select(chunk => chunk.Select(item => item.Value));
}
}