Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeat an enumerable indefinitely

Tags:

c#

linq

Is there an enumerable extension method that repeats the enumerable indefinitely?

So for example, given an enumerable that returns: ["a", "b", "c"]. I would like a method that returns an infinite repeating sequence ["a", "b", "c", "a", "b", "c", "a", "b", "c" ... ]

This sounds a bit like Observable.Repeat, except I would like to operate on IEnumerables.

Enumerable.Repeat only generates an enumerable from a single element.

like image 925
Dave Hillier Avatar asked Jun 12 '14 10:06

Dave Hillier


3 Answers

I don't know of anything built into LINQ, but it's really easy to create your own:

public static IEnumerable<T> RepeatIndefinitely<T>(this IEnumerable<T> source)
{
    while (true)
    {
        foreach (var item in source)
        {
            yield return item;
        }
    }
}

Note that this evaluates source multiple times - you might want to make it only do so once, creating a copy:

public static IEnumerable<T> RepeatIndefinitely<T>(this IEnumerable<T> source)
{
    var list = source.ToList();
    while (true)
    {
        foreach (var item in list)
        {
            yield return item;
        }
    }
}

Notes:

  • Creating a copy of the sequence means the original sequence may be modified freely without worrying about this code iterating over it concurrently.
  • Creating a copy of the sequence means it needs to be sufficiently small to fit in memory, of course. That may not be ideal.
  • This will only create a copy when you start iterating over the result. That could easily be surprising. An alternative approach would be to have a non-iterator method which created a copy, then delegated to a private iterator method. This is the approach used for argument validation in LINQ.
  • The copy is shallow - if the source is a sequence of StringBuilder references, for example, then any changes to the objects themselves will still be visible.
like image 116
Jon Skeet Avatar answered Oct 24 '22 00:10

Jon Skeet


Can't you use Repeat + SelectMany?

var take100ABC = Enumerable.Repeat(new[] { "A", "B", "C" }, 100)
                           .SelectMany(col => col);

In my opinion an extension method is useful only if you need it often. I doubt that you need a RepeatIndefinitely often. But a RepeatWhile could be handy in many cases. You could it also for an infinite repetition.

So here is my my first attempt:

public static IEnumerable<TSource> RepeatWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    TSource item = default(TSource);
    do
    {
        foreach (TSource current in source)
        {
            item = current;
            yield return item;
        }
    }
    while (predicate(item));
    yield break;
}

You can use it for your "infinite" repetion for example in this way:

string[] collection = { "A", "B", "C"};
var infiniteCollection = collection.RepeatWhile(s => s == s);
List<string> take1000OfInfinite = infiniteCollection.Take(1000).ToList();
like image 35
Tim Schmelter Avatar answered Oct 23 '22 23:10

Tim Schmelter


Here is another option if you can use NuGet package of System.Interactive (aka Ix), just use Repeat().

var sequence = Enumerable.Range(1, 3).Repeat();

foreach (var item in sequence.Take(10))
{
    Console.WriteLine(item); // 1, 2, 3, 1, 2, 3, 1, 2, 3, 1
}
like image 4
cactuaroid Avatar answered Oct 24 '22 00:10

cactuaroid