score:1

Accepted answer

You can use MoreLinq for that. There is an experimental function called Memoize which lazily caches the sequence. So the code will look like this:

while (remainingItems.Any())
{
    var result = new List<Item>();
    result.AddRange(remainingItems.TakeWhile(x => somePredicate(x, counter));
    results.Add(result);
    remainingItems = remainingItems.Skip(result.Count).Memoize();
    counter++;
}

Here the result will not be materialized because it is still lazy evaluation:

remainingItems = remainingItems.Skip(result.Count).Memoize();

Here the remainingItems sequence will be evaluated and cached (the iterator will not go through all the elements like in ToList):

remainingItems.Any()

And here the cache will be used:

result.AddRange(remainingItems.TakeWhile(x => somePredicate(x, counter));

To use this method you need to add:

using MoreLinq.Experimental;

score:0

As we are skipping the result set in series why not use the for loop for the same like

 for(int i = 0 ; i < result.Count ; i++){
       //do some business logic and now i got X result
      i = i + X
    }

score:0

Yield might be useful, if I'm understanding your question correctly

public static IEnumerable<IEnumerable<T>> Test<T>(IEnumerable<T> source)
{
    var items = new List<T>();

    foreach (T item in source)
    {
        items.Add(item);

        if (!SomePredicate(item))
        {
            yield return items;
            items = new List<T>();
        }
    }

    // if you want any remaining items to go into their own IEnumerable, even if there's no more fails
    if (items.Count > 0)
    {
        yield return items;
    }
}

Just as en example I made my fail condition to be !item % 10 == 0 and passed in values 0 to 1000 to the above method. I get 101 IEnumerables containing 0 in the first, and the rest containing 1 to 10, 11 to 20, etc. etc.

score:0

You could write a simple extension method to help with this:

public static IEnumerable<IEnumerable<T>> PartitionBy<T>(this IEnumerable<T> sequence, Func<T, int, bool> predicate)
{
    var block = new List<T>();
    int index = 0;

    foreach (var item in sequence)
    {
        if (predicate(item, index++))
        {
            block.Add(item);
        }
        else if (block.Count > 0)
        {
            yield return block.ToList(); // Return a copy so the caller can't change our local list.
            block.Clear();
        }
    }

    if (block.Count > 0)
        yield return block; // No need for a copy since we've finished using our local list.
}

(As an extension method, you need to put that in a static class.)

Then you can use it to partition data like so. For this example, we will partition a list of ints into partitions where the list element's value is equal to its index:

static void Main()
{                         //  0  1  2  3  4  5  6  7  8  9
    var ints = new List<int> {0, 1, 0, 3, 4, 5, 0, 0, 8, 9};

    var result = ints.PartitionBy(((item, index) => item == index)); // Items where value == index.

    foreach (var seq in result)
        Console.WriteLine(string.Join(", ", seq));

    // Output is:
    // 0, 1
    // 3, 4, 5
    // 8, 9
}

Note that this implementation skips over elements that do not match the predicate.


Here's an alternative, more complicated implementation that doesn't make a copy of the data:

class Indexer
{
    public int  Index;
    public bool Finished;
}

public static IEnumerable<IEnumerable<T>> PartitionBy<T>(this IEnumerable<T> sequence, Func<T, int, bool> predicate)
{
    var iter = sequence.GetEnumerator();
    var indexer = new Indexer();

    while (!indexer.Finished)
    {
        yield return nextBlock(iter, predicate, indexer);
    }
}

static IEnumerable<T> nextBlock<T>(IEnumerator<T> iter, Func<T, int, bool> predicate, Indexer indexer)
{
    int index = indexer.Index;
    bool any = false;

    while (true)
    {
        if (!iter.MoveNext())
        {
            indexer.Finished = true;
            yield break;
        }

        if (predicate(iter.Current, index++))
        {
            any = true;
            yield return iter.Current;
        }
        else
        {
            indexer.Index = index;

            if (any)
                yield break;
        }
    }
}

Related Articles