Jak w pętli przez IEnumerable w partiach [duplikat]

to pytanie ma już odpowiedzi tutaj : tworzenie partii w linq (16 odpowiedzi) Zamknięte 1 rok temu .

Rozwijam program c#, który ma "liczbę użytkowników", która przechowuje identyfikatory 4 milionów użytkowników. Muszę wykonać pętlę przez Ienummerable i wyodrębnić batch 1000 id za każdym razem, aby wykonać pewne operacje w innej metodzie.

Jak wyodrębnić 1000 identyfikatorów na raz od początku Ienumerable ... zrobić coś innego potem pobrać następną partię 1000 i tak dalej ?

Czy to możliwe?
Author: Sergey Berezovskiy, 2013-03-14

9 answers

Wygląda na to, że musisz użyć metod Skip i Take swojego obiektu. Przykład:

users.Skip(1000).Take(1000)

To pominie pierwsze 1000 i zajmie następne 1000. Musisz tylko zwiększyć kwotę pomijaną przy każdym połączeniu

Możesz użyć zmiennej całkowitej z parametrem Skip i możesz dostosować, ile jest pomijane. Następnie można go wywołać metodą.

public IEnumerable<user> GetBatch(int pageNumber)
{
    return users.Skip(pageNumber * 1000).Take(1000);
}
 51
Author: Bill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-14 16:19:11

Możesz użyć MoreLINQ ' s Batch operator (dostępny od NuGet):

foreach(IEnumerable<User> batch in users.Batch(1000))
   // use batch

Jeśli proste użycie biblioteki nie jest możliwe, możesz ponownie użyć implementacji:

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

BTW dla wydajności można po prostu zwrócić bucket bez wywołania Select(x => x). Select jest zoptymalizowany pod kątem tablic, ale delegat selektora nadal będzie wywoływany na każdym elemencie. Więc w Twoim przypadku lepiej użyć

yield return bucket;
 152
Author: Sergey Berezovskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-14 13:29:04

Najprostszym sposobem, aby to zrobić, jest prawdopodobnie po prostu użyć GroupBy metoda w LINQ:

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

Ale dla bardziej wyrafinowanego rozwiązania, zobacz ten blog post Jak stworzyć własną metodę rozszerzenia, aby to zrobić. Powielane tutaj dla potomności:

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;
}
 31
Author: p.s.w.g,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-04 15:59:55

Spróbuj użyć tego:

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

I użyć powyższej funkcji:

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

}
 16
Author: Zaki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-14 16:11:45

A może

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);
}
 13
Author: Kabindas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-05 17:42:59

Możesz to osiągnąć używając metody Take I Skip Enumerable extension. Aby uzyskać więcej informacji na temat użycia checkout linq 101

 5
Author: Krishnaswamy Subramanian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-14 16:08:29

Coś takiego by zadziałało:

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
}

I można to uogólnić na metodę generyczną, taką jak Ta:

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);
    }
}
 5
Author: JLRishe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-14 20:18:34
 0
Author: Aghilas Yakoub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-14 16:10:59

W kontekście strumieniowym, gdzie licznik może zostać zablokowany w środku partii, po prostu dlatego, że wartość nie jest jeszcze Wyprodukowana (wydajność), przydatne jest użycie metody timeout, aby ostatnia partia została wyprodukowana po określonym czasie. Użyłem tego na przykład do śledzenia kursora w MongoDB. To trochę skomplikowane, bo wyliczenie musi być wykonane w innym wątku.

    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();
    }
 -1
Author: Mihai Ciureanu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-20 16:22:03