2010-10-06 2 views
3

У меня есть коллекция объектов и вам нужно взять партии из 100 объектов и выполнить некоторую работу с ними, пока не осталось объектов, которые нужно обработать.LINQ: взять последовательность элементов из коллекции

Вместо того, чтобы перебирать каждый элемент и захватывать 100 элементов, тогда следующая сотня и т. Д. Есть ли более удобный способ сделать это с помощью linq?

Большое спасибо

+4

ответ LukeH является гораздо лучше, пожалуйста, принять и использовать его. – Andrey

ответ

10
static void test(IEnumerable<object> objects) 
{ 
    while (objects.Any()) 
    { 
     foreach (object o in objects.Take(100)) 
     { 
     } 
     objects = objects.Skip(100); 
    } 
} 

:)

+0

Будьте осторожны при использовании этого подхода с Entity Framework. Генерируемый SQL будет иметь дополнительный подзапрос на каждой итерации. Если количество пакетов превышает 50, запрос завершается с ошибкой. –

11
int batchSize = 100; 
var batched = yourCollection.Select((x, i) => new { Val = x, Idx = i }) 
          .GroupBy(x => x.Idx/batchSize, 
            (k, g) => g.Select(x => x.Val)); 

// and then to demonstrate... 
foreach (var batch in batched) 
{ 
    Console.WriteLine("Processing batch..."); 

    foreach (var item in batch) 
    { 
     Console.WriteLine("Processing item: " + item); 
    } 
} 
+0

очень неэффективен. GroupBy - это дорогостоящая операция – Andrey

+0

@ Андре: Я предлагаю вам проверить свой собственный ответ против версии GroupBy, прежде чем принимать какие-либо претензии о неэффективности. Результаты могут быть удивлены. – LukeH

+0

Вы правы, ваш метод более совершенен (более 10 раз)! – Andrey

2

Я не думаю, что это на самом деле LINQ подходит для такого рода обработки - это в основном полезно для выполнения операций над целыми последовательностями, а не расщеплением или их модификации. Я сделал бы это, обратившись к базовому IEnumerator<T>, поскольку любой метод с использованием Take и Skip будет довольно неэффективным.

public static void Batch<T>(this IEnumerable<T> items, int batchSize, Action<IEnumerable<T>> batchAction) 
{ 
    if (batchSize < 1) throw new ArgumentException(); 

    List<T> buffer = new List<T>(); 
    using (var enumerator = (items ?? Enumerable.Empty<T>()).GetEnumerator()) 
    { 
     while (enumerator.MoveNext()) 
     { 
      buffer.Add(enumerator.Current); 
      if (buffer.Count == batchSize) 
      { 
       batchAction(buffer); 
       buffer.Clear(); 
      } 
     } 

     //execute for remaining items 
     if (buffer.Count > 0) 
     { 
      batchAction(buffer); 
     } 
    } 
} 
3

Это разделит список на список списков, сколько бы вы ни указали.

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int size) 
{ 
    int i = 0; 
    List<T> list = new List<T>(size); 
    foreach (T item in source) 
    { 
     list.Add(item); 
     if (++i == size) 
     { 
      yield return list; 
      list = new List<T>(size); 
      i = 0; 
     } 
    } 
    if (list.Count > 0) 
     yield return list; 
} 
0
var batchSize = 100; 
for (var i = 0; i < Math.Ceiling(yourCollection.Count()/(decimal)batchSize); i++) 
{ 
    var batch = yourCollection 
     .Skip(i*batchSize) 
     .Take(batchSize); 

    // Do something with batch 
} 
Смежные вопросы