Home > Enterprise >  Skip foreach X positions
Skip foreach X positions

Time:03-09

I want skip my in foreach. For example:

foreach(Times t in timeList)
{ 
      if(t.Time == 20)
      {    
          timeList.Skip(3);   
      }                           
}

I want "jump" 3 positions in my list.. If, in my if block t.Id = 10 after skip I want get t.Id = 13

CodePudding user response:

How about this? If you use a for loop then you can just step the index forward as needed:

for (var x = 0; x < timeList.Length; x  )
{
    if (timeList[x].Time == 20)
    {
        // option 1
        x  = 2; // 'x  ' in the for loop will  1, 
                // we are adding  2 more to make it 3? 

        // option 2
        // x  = 3; // just add 3!
    }
}

CodePudding user response:

You can't modify an enumerable in-flight, as it were, like you could the index of a for loop; you must account for it up front. Fortunately there are several way to do this.

Here's one:

foreach(Times t in timeList.Where(t => t.Time < 20 || t.Time > 22))
{ 
 
}

There's also the .Skip() option, but to use it you must break the list into two separate enumerables and then rejoin them:

var times1 = timeList.TakeWhile(t => t.Time != 20);
var times2 = timeList.SkipeWhile(t => t.Time != 20).Skip(3);
foreach(var t in times1.Concat(times2))
{

}

But that's not exactly efficient, as it requires iterating over the first part of the sequence twice (and won't work at all for Read Once -style sequences). To fix this, you can make a custom enumerator:

public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> items, Predicate<T> SkipTrigger, int SkipCount)
{
    bool triggered = false;
    int SkipsRemaining = 0;

    var e = items.GetEnumerator();
    while (e.MoveNext())
    {
        if (!triggered && SkipTrigger(e.Current))
        {
            triggered = true;
            SkipsRemaining = SkipCount;
        }

        if (triggered)
        {
             SkipsRemaining--;
             if (SkipsRemaining == 0) triggered = false;
        } 
        else 
        {
             yield return e.Current; 
        }
    }
}

Then you could use it like this:

foreach(Times t in timeList.SkipAt(t => t.Times == 20, 3))
{ 
                           
}

But again: you still need to decide about this up front, rather than inside the loop body.

For fun, I felt like adding an overload that uses another predicate to tell the enumerator when to resume:

public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> items, Predicate<T> SkipTrigger, Predicate<T> ResumeTrigger)
{
    bool triggered = false;

    var e = items.GetEnumerator();
    while (e.MoveNext())
    {
        if (!triggered && SkipTrigger(e.Current))
        {
            triggered = true;
        }

        if (triggered)
        {
             if (ResumeTrigger(e.Current)) triggered = false;               
        } 
        else 
        {
             yield return e.Current; 
        }
    }
}

CodePudding user response:

You can use continue with some simple variables.

int skipCount = 0;
bool skip = false;

foreach (var x in myList)
{
    if (skipCount == 3)
    {
        skip = false;
        skipCount = 0;
    }
    
    if (x.time == 20)
    {
        skip = true;
        skipCount = 0;
    }

    if (skip)
    {
        skipCount  ;
        continue;
    }
    
    // here you do whatever you don't want to skip
}

Or if you can use a for-loop, increase the index like this:

for (int i = 0; i < times.Count)
{
    if (times[i].time == 20)
    {
        i  = 2;
        continue; // -1 skip for this continue
    }

    // here you do whatever you don't want to skip
}
  •  Tags:  
  • c#
  • Related