Как перемещаться по IEnumerable пакетами

Я разрабатываю программу С#, которая имеет "IEnumerable users", в которой хранятся идентификаторы из 4 миллионов пользователей. Мне нужно прокручивать Ienummerable и каждый раз извлекать количество 1000 идентификаторов для выполнения некоторых операций в другом методе.

Как извлечь 1000 идентификаторов за раз с момента запуска Ienumerable... сделать что-то еще, а затем выбрать следующую партию 1000 и так далее?

Возможно ли это?

Ответ 1

Похоже, вам нужно использовать методы Skip и Take вашего объекта. Пример:

users.Skip(1000).Take(1000)

это пропустит первый 1000 и займет следующий 1000. Вам просто нужно увеличить количество, пропущенное с каждым вызовом

Вы можете использовать целочисленную переменную с параметром для Пропустить, и вы можете настроить, сколько пропущено. Затем вы можете вызвать его в методе.

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

Ответ 2

Вы можете использовать morelinq Batch метод (доступен от 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);            
}

BTW для производительности вы можете просто вернуть ведро без вызова Select(x => x). Выбор оптимизирован для массивов, но делегат-селектор все равно будет вызываться для каждого элемента. Итак, в вашем случае лучше использовать

yield return bucket;

Ответ 3

Самый простой способ сделать это - это просто использовать метод 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 comment below
        }
    }

    if (nextbatch.Count > 0)
        yield return nextbatch;
}

Ответ 4

попробуйте использовать это:

  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))
{

}

Ответ 5

Вы можете добиться этого с помощью метода расширения и прокрутки Enumerable. Для получения дополнительной информации об использовании checkout linq 101

Ответ 6

Что-то вроде этого будет работать:

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