Как организовать цикл через IEnumerable пачками
Я разрабатываю программу на c#, которая имеет "IEnumerable пользователей", который хранит идентификаторы 4 миллионов пользователей. Мне нужно перебрать ienummerable и извлечь пакет 1000 идентификаторов каждый раз, чтобы выполнить некоторые операции в другом методе.
Как я могу извлечь 1000 идентификаторов одновременно с начала Ienumerable ...сделать что-то еще, а затем получить следующую партию 1000 и так далее ?
это возможно?
8 ответов:
звучит так, как будто вам нужно использовать методы Skip и Take вашего объекта. Пример:
users.Skip(1000).Take(1000)это пропустит первую 1000 и возьмет следующую 1000. Вам просто нужно увеличить количество пропущенных с каждым вызовом
Вы можете использовать целочисленную переменную с параметром Skip и вы можете настроить, как много пропускается. Затем вы можете вызвать его в методе.
public IEnumerable<user> GetBatch(int pageNumber) { return users.Skip(pageNumber * 1000).Take(1000); }
можно использовать пакетный оператор MoreLINQ (доступно из NuGet):
foreach(IEnumerable<User> batch in users.Batch(1000)) // use batch
если простое использование библиотеки не является опцией, вы можете повторно использовать реализацию:
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) yield return bucket.Take(count); }кстати, для производительности вы можете просто вернуть ведро без вызова
Select(x => x). Select оптимизирован для массивов, но делегат селектора все равно будет вызываться для каждого элемента. Так что, в вашем случае лучше использоватьyield return bucket;
самый простой способ сделать это, вероятно, просто использовать
GroupByметод в 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; }
попробуйте использовать этот:
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; }и использовать вышеуказанную функцию:
foreach (var list in Users.Batch(1000)) { }
вы можете достичь этого с помощью метода Take и Skip Enumerable extension. Для получения дополнительной информации об использовании checkout linq 101
что-то вроде этого будет работать:
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); } }
можно использовать
Take operator linqСсылка:http://msdn.microsoft.com/fr-fr/library/vstudio/bb503062.aspx
как о
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); }
Comments