14

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.

Dave Hillier
  • 18,105
  • 9
  • 43
  • 87

6 Answers6

23

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.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Wouldn't it better to try-cast it to a collection before `.ToList` is used to create another collection? But i must admit that i've not understood the reason for `ToList` at all. Maybe you want to elaborate on this a little bit. – Tim Schmelter Jun 12 '14 at 10:10
  • 1
    @TimSchmelter: Well, `ToList` will always create a copy, which means there definitely *won't* be any issues iterating over the result while other code modifies the original collection. I don't really have time to go into this in a massive amount of detail in this answer, but I'll add *some* explanatory text. – Jon Skeet Jun 12 '14 at 10:13
  • You could always take an optional `bool`, e.g. `createCopy`, to get the best of both worlds. – Wai Ha Lee Apr 08 '15 at 15:52
6

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();
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
4

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
}
cactuaroid
  • 496
  • 1
  • 4
  • 19
2

You can create a simple RepeatForever extension and use it on a sequence, then SelectMany over the sequence of sequences to flatten it.

public static IEnumerable<T> RepeatForever<T>(this T item)
{
    for(;;) yield return item;
}
public static IEnumerable<T> RepeatSequenceForever<T>(this IEnumerable<T> seq)
{
    return seq.RepeatForever().SelectMany(x => x);
}
spender
  • 117,338
  • 33
  • 229
  • 351
0

If you don't mind an extra dependency you could use the Repeat method from MoreLinq

It does exactly what you need. For example:

    var sequence = new[] {"a", "b", "c"};
    var generator = sequence.Repeat();
    
    var boundSequence = Enumerable.Range(0,10).Zip(generator);
    
    foreach (var (i,val) in boundSequence)
    {
        Console.WriteLine($"{i}: {val}");
    }

prints this:

    0: a
    1: b
    2: c
    3: a
    4: b
    5: c
    6: a
    7: b
    8: c
    9: a
Zar Shardan
  • 5,675
  • 2
  • 39
  • 37
-1

Same solution as the accepted answer, but I wanted to avoid fatal spinning on an empty enumerable, or one that has side effects that exhaust it.

public static IEnumerable<T> RepeatIndefinitely<T>(this IEnumerable<T> source) {
    var t = source.GetEnumerator();
    while (t.MoveNext()) {
        do {
            yield return t.Current;
        } while (t.MoveNext());
        t = source.GetEnumerator();
    }
    throw new InvalidOperationException("Sequence contains no elements, possibly after reset.");
}
shannon
  • 8,664
  • 5
  • 44
  • 74
  • *The Reset method is provided for COM interoperability. It does not necessarily need to be implemented; instead, the implementer can simply throw a NotSupportedException.* [citation](https://learn.microsoft.com/en-us/dotnet/api/system.collections.ienumerator.reset#remarks) – Theodor Zoulias Feb 08 '20 at 23:12
  • Appreciate the comment, happy to refactor... there, does that address it? You read the reason for the additional answer, right? I think it's relevant to those using the accepted answer. – shannon Feb 10 '20 at 04:18
  • If it addresses the defect, consider revising your downvote. – shannon Feb 10 '20 at 04:25
  • Now it's much better, but I still dislike that the enumerators are left undisposed. [*Always dispose your enumerators. They implement IDisposable for a reason.*](https://stackoverflow.com/questions/21337340/enumerator-disposal-when-not-using-using-foreach-or-manually-calling-dispose/21337552#21337552) – Theodor Zoulias Feb 10 '20 at 05:42