HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Cycle through an IEnumerable

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
cycleienumerablethrough

Problem

I built an extension method to cycle through all items of an IEnumerable starting at some index:

public static IEnumerable Circle(this IEnumerable list, int startIndex)
{
    if (list != null)
    {
        List firstList = new List();
        using (var enumerator = list.GetEnumerator())
        {
            int i = 0;
            while (enumerator.MoveNext())
            {
                if (i < startIndex)
                {
                    firstList.Add(enumerator.Current);
                    i++;
                }
                else
                {
                    yield return enumerator.Current;
                }
            }
        }
        foreach (var first in firstList)
        {
            yield return first;
        }
    }
    yield break;
}


So when you do

Enumerable.Range(1,10).Circle(5);


The result is 6,7,8,9,10,1,2,3,4,5.

What I don't like is the use of the firstList variable. Is there a way to do this with the enumerator only, without storing an intermediate result?

EDIT:

I use an enumerator to iterate the enumerable not more than once.

Solution

A shorter form of Trevor's answer (but essentially doing the same thing):

public static IEnumerable Circle(this IEnumerable list, int startIndex)
{
    return list.Skip(startIndex).Concat(list.Take(startIndex));
}


This will still lazy evaluate just like Trevor's answer, because Concat lazily evaluates.

Further more, if you have written a method to cycle through an IEnumerable, why not call it Cycle?

Lastly, I'd actually recommend taking advantage of lazy evaluation to give you a more useful method. The one below will continue to cycle indefinitely, starting with an optional index:

public static IEnumerable Cycle(this IEnumerable list, int index = 0)
{

    var count = list.Count();
    index = index % count;

    while(true)
    {
        yield return list.ElementAt(index);
        index = (index + 1) % count;
    }
}


Then you can do something like:

foreach(var num in Enumerable.Range(1, 10).Cycle(4).Take(30))
{
    Console.WriteLine(num.ToString());
}


And this lets you specify just how many repeated items you want.

Code Snippets

public static IEnumerable<T> Circle<T>(this IEnumerable<T> list, int startIndex)
{
    return list.Skip(startIndex).Concat(list.Take(startIndex));
}
public static IEnumerable<T> Cycle<T>(this IEnumerable<T> list, int index = 0)
{

    var count = list.Count();
    index = index % count;

    while(true)
    {
        yield return list.ElementAt(index);
        index = (index + 1) % count;
    }
}
foreach(var num in Enumerable.Range(1, 10).Cycle(4).Take(30))
{
    Console.WriteLine(num.ToString());
}

Context

StackExchange Code Review Q#9088, answer score: 10

Revisions (0)

No revisions yet.