Home > Software design >  Iterate starting from a preset position and continue iterating after end is reached
Iterate starting from a preset position and continue iterating after end is reached

Time:09-22

I have seen a similar example with loops continuing after the last element to reset to first, which is great. However, I am unable to figure out how to add that PLUS start iterating from a specified index of the IList object.

Assume we have this IList object with a count of 6. (To simplify I'm only using indices below instead of the full objects.)

[1, 2, 3, 4, 5, 6]

Now assume we need to iterate over this IList 10 times.

=> Iterate 10 times

The expected output based on the above should be:

=> 1, 2, 3, 4, 5, 6, 1, 2, 3, 4

However I'd like to start iterating from #4 for example, so the expected output should now be:

=> 4, 5, 6, 1, 2, 3, 4, 5, 6, 1

(All the numbers above represent the position of the elements in the collection, not the actual objects as ints)

Any ideas on how this is possible with LINQ and C#? Please keep in mind that we're working with an IList.

CodePudding user response:

You could write the following extension method:

public static class Extensions
{
    public static IEnumerable<T> Iterate<T>(this IList<T> input, int from, int length)
    {
        for(int i = from; i < from   length; i  )
        {
            yield return input[i % input.Count];
        }
    }
}

The modulo division makes sure that you start at 0 again if your index is bigger than the size of the list.

Online demo: https://dotnetfiddle.net/Geqslv

CodePudding user response:

I suggest implementing an extension method for looping again and again:

// Let it be general case with IEnumerable<T>
public static class EnumerableExtensions {
  public static IEnumerable<T> Loop<T>(this IEnumerable<T> source) {
    if (null == source)
      throw new ArgumentNullException(name(source));

    List<T> list = new List<T>();

    foreach (var item in source) {
      yield return item;

      list.Add(item);
    }

    // ist.Count > 0 - we can't loop (again and again) over empty list
    for (int i = 0; list.Count > 0; i = (i   1) % list.Count)
      yield return list[i]; 
  }
}

Then you can put

 List<int> myList = ...

 // we start looping infinitely - Loop()
 // but take first 10 items only - Take(10) 
 foreach (var item in myList.Loop().Take(10)) {
   ...
 }

 // we start looping infinitely - Loop()
 // skip first 4 items - Skip(4)
 // then take 11 items only - Take(11) 
 foreach (var item in myList.Loop().Skip(4).Take(11)) {
   ...
 }

CodePudding user response:

You can use Skip to skip required number of first elements. For example:

public IEnumerable<T> GetRolledItems<T>(IList<T> source, int count, int startIndex)
{
  return Enumerable.Repeat(source, (count   startIndex) / source.Count()   1)
    .SelectMany(a => a)
    .Skip(startIndex)
    .Take(count);
}
  • Related