如何批量循环IEnumerable

use*_*912 73 c# ienumerable

我正在开发一个ac#program,它有一个"IEnumerable users",可以存储400万用户的ID.我需要遍历Ienummerable并每次提取批量1000个ID以在另一个方法中执行某些操作.

如何从Ienumerable开始一次提取1000个ID ...做一些其他事情然后获取下一批1000等等?

这可能吗?

Ser*_*kiy 129

您可以使用MoreLINQ的Batch运算符(可从NuGet获得):

foreach(IEnumerable<User> batch in users.Batch(1000))
   // use batch
Run Code Online (Sandbox Code Playgroud)

如果不能选择简单使用库,则可以重用实现:

public static IEnumerable<IEnumerable<T>> Batch<T>(
        this IEnumerable<T> source, int size)
{
    T[] bucket = null;
    var count = 0;

    foreach (var item in source)
    {
       if (bucket == null)
           bucket = new T[size];

       bucket[count++] = item;

       if (count != size)                
          continue;

       yield return bucket.Select(x => x);

       bucket = null;
       count = 0;
    }

    // Return the last bucket with all remaining elements
    if (bucket != null && count > 0)
    {
        Array.Resize(ref bucket, count);
        yield return bucket.Select(x => x);
    }
}
Run Code Online (Sandbox Code Playgroud)

BTW的性能你可以简单地返回桶而不需要调用Select(x => x).Select针对数组进行了优化,但仍会在每个项目上调用选择器委托.所以,在你的情况下,最好使用

yield return bucket;
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,"Select"是专门用来模糊来自调用者的底层数组(并不是说他们在那时可以做很多事情).大多数人都不需要它,但对于库方法,它可以帮助他们避免在未来改变实施时破坏更改. (8认同)
  • 而不是bucket.Select(x => x)使得调用者无法将IEnumerable <T>转换回原始的T []数组,bucket.Skip(0)会有更好的性能吗?传递0意味着它将每个项目作为IEnumerable <T>的不可变序列返回(它不能被调用者强制转换回T []). (2认同)

Bil*_*ill 42

听起来你需要使用对象的Skip和Take方法.例:

users.Skip(1000).Take(1000)
Run Code Online (Sandbox Code Playgroud)

这将跳过前1000并接下来的1000.你只需要增加每次调用跳过的数量

您可以使用带跳过参数的整数变量,并可以调整跳过的数量.然后,您可以在方法中调用它.

public IEnumerable<user> GetBatch(int pageNumber)
{
    return users.Skip(pageNumber * 1000).Take(1000);
}
Run Code Online (Sandbox Code Playgroud)

  • 效率不高.您始终从头开始并逐步完成之前的结果.更有效的方法是保留您停止的IEnumerable. (20认同)

p.s*_*w.g 29

最简单的方法是使用GroupByLINQ中的方法:

var batches = myEnumerable
    .Select((x, i) => new { x, i })
    .GroupBy(p => (p.i / 1000), (p, i) => p.x);
Run Code Online (Sandbox Code Playgroud)

但是对于更复杂的解决方案,请参阅此博客文章,了解如何创建自己的扩展方法来执行此操作.这里重复了后人:

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> collection, int batchSize)
{
    List<T> nextbatch = new List<T>(batchSize);
    foreach (T item in collection)
    {
        nextbatch.Add(item);
        if (nextbatch.Count == batchSize)
        {
            yield return nextbatch;
            nextbatch = new List<T>(); 
            // or nextbatch.Clear(); but see Servy's comment below
        }
    }

    if (nextbatch.Count > 0)
        yield return nextbatch;
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,通过不每次创建新列表而是清除它,强制调用者在*请求*第二个之前枚举每个批处理.如果他没有,当他认为他列举了第一批时,他最终会得到第二批的价值.另请注意,在GC工作方面您节省的很少,因为清除列表仍将释放内部缓冲区,这是昂贵的部分.虽然在某些情况下它可能会节省一点点,但功能上的重大损失通常是不值得的. (2认同)

小智 17

怎么样

int batchsize = 5;
List<string> colection = new List<string> { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
for (int x = 0; x < Math.Ceiling((decimal)colection.Count / batchsize); x++)
{
    var t = colection.Skip(x * batchsize).Take(batchsize);
}
Run Code Online (Sandbox Code Playgroud)


Zak*_*aki 11

试着用这个:

  public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
        this IEnumerable<TSource> source,
        int batchSize)
    {
        var batch = new List<TSource>();
        foreach (var item in source)
        {
            batch.Add(item);
            if (batch.Count == batchSize)
            {
                 yield return batch;
                 batch = new List<TSource>();
            }
        }

        if (batch.Any()) yield return batch;
    }
Run Code Online (Sandbox Code Playgroud)

并使用上述功能:

foreach (var list in Users.Batch(1000))
{

}
Run Code Online (Sandbox Code Playgroud)

  • 通过向列表构造函数提供批处理大小,可以获得较小的效率提升. (6认同)

Kri*_*ian 5

您可以使用Take和Skip Enumerable扩展方法来实现.有关使用情况结帐linq 101的更多信息

  • @user1526912如果您使用.Net Framework 4.0或更高版本,您可以使用[PLINQ](http://msdn.microsoft.com/en-us/library/dd460688.aspx)利用多核处理器来更快地处理它 (2认同)

JLR*_*she 5

这样的事情会起作用:

List<MyClass> batch = new List<MyClass>();
foreach (MyClass item in items)
{
    batch.Add(item);

    if (batch.Count == 1000)
    {
        // Perform operation on batch
        batch.Clear();
    }
}

// Process last batch
if (batch.Any())
{
    // Perform operation on batch
}
Run Code Online (Sandbox Code Playgroud)

您可以将其概括为通用方法,如下所示:

static void PerformBatchedOperation<T>(IEnumerable<T> items, 
                                       Action<IEnumerable<T>> operation, 
                                       int batchSize)
{
    List<T> batch = new List<T>();
    foreach (T item in items)
    {
        batch.Add(item);

        if (batch.Count == batchSize)
        {
            operation(batch);
            batch.Clear();
        }
    }

    // Process last batch
    if (batch.Any())
    {
        operation(batch);
    }
}
Run Code Online (Sandbox Code Playgroud)