C# 有没有更好的方法将Lambda用于N组?

C# 有没有更好的方法将Lambda用于N组?,c#,lambda,C#,Lambda,我有一个方法Process(IEnumerable 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())

我有一个方法
Process(IEnumerable 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
var\u Records=Enumerable.Range(1,16).ToArray();
对于(int i=0;i
它是有效的,但是。。。有更好的方法吗?

您可以使用的
Batch

var result=Enumerable.Range(1, 16).Batch(3);


如果您想查看它,请参阅下面的扩展方法。

您可以使用此扩展方法:

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));
}
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);
  }

}
如果您需要在解决方案中多次“分页”,则可以考虑使用扩展方法。

在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);
        }
    }
}
将屈服

{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10}}

注意:上面的代码很短并且相对高效,但是仍然没有达到它所能达到的效率(它将在幕后构建一个哈希表)。一个稍微麻烦但更快的方法是:

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);
公共静态类EnumerableEx{
公共静态void分页(此IEnumerable元素、int页面大小、操作过程页面){
var buff=新的T[3];
int指数=0;
foreach(元素中的var元素){
如果(索引==buff.Length){
处理页面(浅黄色);
指数=0;
}
buff[index++]=元素;
}
如果(索引>0)
进程页面(buff.Take(index));
}
}
// ...
var_Records=Enumerable.Range(1,16).ToArray();
_记录。分页(3,过程);

您可以创建自己的扩展方法:

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));
}
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);
  }

}
静态类扩展{
公共静态IEnumerable ToBlocks(此IEnumerable源,int blockSize){
var计数=0;
T[]block=null;
foreach(源中的var项){
if(block==null)
块=新的T[块大小];
块[count++]=项;
如果(计数==块大小){
屈服回程块;
block=null;
计数=0;
}
}
如果(计数>0)
产量返回块。获取(计数);
}
}

下面是另一种LINQ-y方法:

var batchSize = 3;
Enumerable.Range(0, (_Records.Length - 1)/batchSize + 1)
    .ToList()
    .ForEach(i => Process(_Records.Skip(i * batchSize).Take(batchSize)));

此扩展方法工作正常

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));
    }
}
公共静态类枚举扩展
{
公共静态IEnumerable块(此IEnumerable项,整数大小)
{
返回
items.Select((成员,索引)=>new{index=index,Value=member})
.GroupBy(item=>(int)item.Index/size)
.Select(chunk=>chunk.Select(item=>item.Value));
}
}

总有一个比在循环内部断开更好的方法……您可以在内部断开条件下更改
for
。“它会更短更干净。”穆阿利格说。显而易见的答案是+1这是非常低效的-在循环的每个迭代中,您有两个调用
跳过
(和
获取
)。一个在
Any
中,一个在
过程中(由于延迟执行)。由于每个
Skip
平均迭代大约N/2个元素,整个过程是O(N^2)。哇,你真的简化了它。;)如果你对整体不感兴趣,你知道,那就是清晰的事情
var batchSize = 3;
Enumerable.Range(0, (_Records.Length - 1)/batchSize + 1)
    .ToList()
    .ForEach(i => Process(_Records.Skip(i * batchSize).Take(batchSize)));
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);
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));
    }
}