0

i have a collection with thousands of elements, i do a for loop to process 100 elements at a time and then upload them and pause for 5 seconds

while (EntriesList.Count > 0)
            {
                service.InsertEntries(EntriesList.Take(100).ToList<Entry>());
                System.Threading.Thread.Sleep(5000);
            }

service.InsertEntries takes a List<Entry> as a parameter, also is there a better way to achive such thing?

normally i go with a for loop with a % operator and a temp list, is the above the same?

7
  • 1
    I usually write myself a AsChunked(int batchSize) method that converts a list into a sequence of batches. Commented Nov 6, 2013 at 12:21
  • ps: "Take" function does not remove elements from the list => Your 'while' will never remove anything from EntriesList. Normal ? Commented Nov 6, 2013 at 12:23
  • @Olivier yes i don't want to remove them! Commented Nov 6, 2013 at 12:24
  • You dont? Then EntriesList.Count > 0 will always be true. Commented Nov 6, 2013 at 12:25
  • unless there is another thread modifying it... but I can see kind of a conccurency issue :) Commented Nov 6, 2013 at 12:26

5 Answers 5

3

This will insert the first 100 entries again and again.

Keep track of where you are and use use Skip to move forward:

int current = 0;
int batchSize = 100;

while (current < EntriesList.Count)
{
    service.InsertEntries(EntriesList.Skip(current).Take(batchSize).ToList());
    current += batchSize;
    System.Threading.Thread.Sleep(5000);
}
Sign up to request clarification or add additional context in comments.

Comments

2

For this kind of thing you should write a Chunkify extension method. One good example you can get from Marc here. But due to the question it always returns an array with a fixed size and maybe null entries.

So maybe this implementation matches better your requirements:

/// <summary>
/// Divides an enumeration into smaller (same-sized) chunks.
/// </summary>
/// <typeparam name="T">The type of the elements within the sequence.</typeparam>
/// <param name="source">The sequence which should be breaked into chunks.</param>
/// <param name="size">The size of each chunk.</param>
/// <returns>An IEnumerable&lt;T&gt; that contains an IEnumerable&lt;T&gt; for each chunk.</returns>
public static IEnumerable<IEnumerable<T>> Chunkify<T>(this IEnumerable<T> source, int size)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }

    if (size < 1)
    {
        throw new ArgumentOutOfRangeException("size");
    }

    return ChunkifyImpl(source, size);
}

private static IEnumerable<IEnumerable<T>> ChunkifyImpl<T>(IEnumerable<T> source, int size)
{
    using (var iter = source.GetEnumerator())
    {
        while (iter.MoveNext())
        {
            var chunk = new List<T>(size);
            chunk.Add(iter.Current);

            for (int i = 1; i < size && iter.MoveNext(); i++)
            {
                chunk.Add(iter.Current);
            }

            yield return chunk;
        }
    }
}

Now you can use this extension as follows:

foreach(var chunk in EntriesList.Chunkify(100))
{
    service.InsertEntries(chunk);
    Thread.Sleep(5000);
}

Comments

2

EntriesList.Take(100).ToList() will create a new list of 100 entries, you will not remove the 100 entries from your EntriesList.

You can do something like:

List<TYPE> objectsforupload = EntriesList.ToList();
while (objectsforupload.Count > 0)
{
        List<TYPE> uploadlist = objectsforupload.Take(100).ToList();
        objectsforupload = objectsforupload.Where(x => !uploadlist.Contains(x)).ToList();
        service.InsertEntries(objectsforupload);
        System.Threading.Thread.Sleep(5000);
}

Comments

2

Call this after the .Take()

EntriesList.RemoveRange(0, Math.Min(EntriesList.Count, 100));

It will remove the first 100 elements.

EDIT: more complete example:

while (EntriesList.Count > 0)
{
    var poppedEntries = EntriesList.Take(100).ToList<Entry>();
    EntriesList.RemoveRange(0, poppedEntries.Count);
    service.InsertEntries(poppedEntries);
    System.Threading.Thread.Sleep(5000);
}

Comments

1

As it stands, your code does not work, you should also Skip by the same amount and

        while (entries.Count > 0)
        {
            service.InsertEntries(entries.Take(100).ToList<Entry>());
            entries = entries.Skip(100);
            System.Threading.Thread.Sleep(5000);
        }

However, this performs very badly. the IEnumable<> you get back from Skip() will add overhead every time it is called.

So the best advice for this scenario is to use a for loop.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.