C# 如何成批循环使用IEnumerable
我正在开发一个c#程序,它有一个“IEnumerable users”,存储400万用户的ID。我需要循环遍历IEnumerable,每次提取一批1000个ID,以在另一个方法中执行一些操作 如何从Ienumerable开始一次提取1000个ID…做一些其他事情,然后获取下一批1000个ID,依此类推C# 如何成批循环使用IEnumerable,c#,ienumerable,C#,Ienumerable,我正在开发一个c#程序,它有一个“IEnumerable users”,存储400万用户的ID。我需要循环遍历IEnumerable,每次提取一批1000个ID,以在另一个方法中执行一些操作 如何从Ienumerable开始一次提取1000个ID…做一些其他事情,然后获取下一批1000个ID,依此类推 这可能吗?最简单的方法可能就是使用LINQ中的方法: var batches = myEnumerable .Select((x, i) => new { x, i }) .
这可能吗?最简单的方法可能就是使用LINQ中的方法:
var batches = myEnumerable
.Select((x, i) => new { x, i })
.GroupBy(p => (p.i / 1000), (p, i) => p.x);
但是对于一个更复杂的解决方案,请参阅下面关于如何创建自己的扩展方法来实现这一点的内容。为子孙后代复制于此:
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;
}
公共静态IEnumerable批处理(此IEnumerable集合,int batchSize)
{
List nextbatch=新列表(批量大小);
foreach(集合中的T项)
{
下一批次添加(项目);
if(nextbatch.Count==batchSize)
{
下一批收益率;
nextbatch=新列表();
//或nextbatch.Clear();但请参见下面的Servy评论
}
}
如果(nextbatch.Count>0)
下一批收益率;
}
您可以使用Take和Skip可枚举扩展方法来实现这一点。有关用法签出的更多信息,请使用(可从NuGet获得):
听起来您需要对对象使用Skip和Take方法。例如:
users.Skip(1000).Take(1000)
这将跳过前1000个,然后取下1000个。你只需要增加每次通话跳过的次数
您可以使用带有Skip参数的整数变量,并可以调整跳过的数量。然后可以在方法中调用它
public IEnumerable<user> GetBatch(int pageNumber)
{
return users.Skip(pageNumber * 1000).Take(1000);
}
public IEnumerable GetBatch(int页码)
{
返回用户。跳过(页码*1000)。获取(1000);
}
您可以使用Take运算符linq
链接:尝试使用以下链接:
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;
}
类似这样的方法会奏效:
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
}
List批处理=新建列表();
foreach(项目中的MyClass项目)
{
批次。添加(项目);
如果(batch.Count==1000)
{
//对批执行操作
batch.Clear();
}
}
//处理最后一批
if(batch.Any())
{
//对批执行操作
}
您可以将其概括为一个通用方法,如下所示:
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);
}
}
static void performbatch操作(IEnumerable items,
行动行动,,
int批处理大小)
{
列表批次=新列表();
foreach(项目中的T项目)
{
批次。添加(项目);
if(batch.Count==batchSize)
{
操作(批量);
batch.Clear();
}
}
//处理最后一批
if(batch.Any())
{
操作(批量);
}
}
怎么样
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);
}
int batchsize=5;
列表集合=新列表{“1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9”、“10”、“11”、“12”};
对于(int x=0;x<数学上限((十进制)colection.Count/batchsize);x++)
{
var t=collection.Skip(x*batchsize).Take(batchsize);
}
在流媒体环境中,枚举器可能会在批处理的中间被阻塞,仅仅因为该值尚未产生(产率),所以有一个超时方法是有用的,以便在给定的时间之后产生最后一批。例如,我用它来跟踪MongoDB中的光标。这有点复杂,因为枚举必须在另一个线程中完成
public static IEnumerable<List<T>> TimedBatch<T>(this IEnumerable<T> collection, double timeoutMilliseconds, long maxItems)
{
object _lock = new object();
List<T> batch = new List<T>();
AutoResetEvent yieldEventTriggered = new AutoResetEvent(false);
AutoResetEvent yieldEventFinished = new AutoResetEvent(false);
bool yieldEventTriggering = false;
var task = Task.Run(delegate
{
foreach (T item in collection)
{
lock (_lock)
{
batch.Add(item);
if (batch.Count == maxItems)
{
yieldEventTriggering = true;
yieldEventTriggered.Set();
}
}
if (yieldEventTriggering)
{
yieldEventFinished.WaitOne(); //wait for the yield to finish, and batch to be cleaned
yieldEventTriggering = false;
}
}
});
while (!task.IsCompleted)
{
//Wait for the event to be triggered, or the timeout to finish
yieldEventTriggered.WaitOne(TimeSpan.FromMilliseconds(timeoutMilliseconds));
lock (_lock)
{
if (batch.Count > 0) //yield return only if the batch accumulated something
{
yield return batch;
batch.Clear();
yieldEventFinished.Set();
}
}
}
task.Wait();
}
public静态IEnumerable TimedBatch(此IEnumerable集合,双timeoutmillizes,长maxItems)
{
对象_lock=新对象();
列表批次=新列表();
AutoResetEvent yieldEventTriggered=新的AutoResetEvent(假);
AutoResetEvent yieldEventFinished=新的AutoResetEvent(假);
bool yieldEventTriggering=false;
var task=task.Run(委托
{
foreach(集合中的T项)
{
锁
{
批次。添加(项目);
if(batch.Count==maxItems)
{
yieldEventTriggering=true;
yieldEventTriggered.Set();
}
}
如果(yieldEventTriggering)
{
yieldEventFinished.WaitOne();//等待成品完成,并清理批次
yieldEventTriggering=false;
}
}
});
而(!task.IsCompleted)
{
//等待事件被触发,或等待超时完成
yieldEventTriggered.WaitOne(TimeSpan.frommilluses(timeoutmiluses));
锁
{
if(batch.Count>0)//仅当批次累积了某些内容时才返回产量
{
退货批量;
batch.Clear();
yieldEventFinished.Set();
}
}
}
task.Wait();
}
是的,但可能效率不高……我知道可以使用take,但当我获取1000条记录时,如何从上一条记录中再获取1000条point@user1526912-跳过(i*1000)。在i=0,1,2时取(1000)
,…@user1526912如果您使用的是.Net framework 4.0或更高版本,您可以使用多核处理器来稍微快速地处理它。这对于LINQ to somequeryprovider非常有用,可以在其中高效地实现跳过。对于LINQ To对象,这将对每个批都迭代源序列一次,但会花费大量时间在Skip
中,因为它对任意序列都不有效。我正要回答这个问题。不要重新发明轮子,使用MoreLinq的批处理方法;-)@Meta Knight是的,我使用他们的MaxBy、DistinctBy、Batch和其他方法。发明新车比改造车轮要好:)值得注意的是,Select
专门用于向调用者隐藏底层数组(并不是说他们在这一点上可以做很多)。
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
}
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);
}
}
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);
}
public static IEnumerable<List<T>> TimedBatch<T>(this IEnumerable<T> collection, double timeoutMilliseconds, long maxItems)
{
object _lock = new object();
List<T> batch = new List<T>();
AutoResetEvent yieldEventTriggered = new AutoResetEvent(false);
AutoResetEvent yieldEventFinished = new AutoResetEvent(false);
bool yieldEventTriggering = false;
var task = Task.Run(delegate
{
foreach (T item in collection)
{
lock (_lock)
{
batch.Add(item);
if (batch.Count == maxItems)
{
yieldEventTriggering = true;
yieldEventTriggered.Set();
}
}
if (yieldEventTriggering)
{
yieldEventFinished.WaitOne(); //wait for the yield to finish, and batch to be cleaned
yieldEventTriggering = false;
}
}
});
while (!task.IsCompleted)
{
//Wait for the event to be triggered, or the timeout to finish
yieldEventTriggered.WaitOne(TimeSpan.FromMilliseconds(timeoutMilliseconds));
lock (_lock)
{
if (batch.Count > 0) //yield return only if the batch accumulated something
{
yield return batch;
batch.Clear();
yieldEventFinished.Set();
}
}
}
task.Wait();
}