protango
protango

Reputation: 1322

For loop between two numbers where direction is unknown

I'm writing a graphics program in C# and I couldn't figure out a good way to run a for loop between two values, where either one may be larger or smaller than the other.

To demonstrate, the following code works perfectly when X2>X1:

for (int x = X1; x<=X2; x++) {
    //code
}

However, it fails when X2<X1. What I want to happen in this situation is that the loop starts at X1 and goes backwards until X2.

I since I'm doing a graphics program, I can't simply swap X1 and X2 when X2<X1, as this would mean swapping their associated Y values, which could produce the same problem just for Y values. The loop must always start at X1, it's the direction(+/-) that needs to change, not the order of values.

I've thought of a few solutions however they all have flaws, it's worth noting that X1 will never equal X2.

#1: Replicate loop

if (X2<X1) {
    for (int x = X1; x>=X2; x--) {/*code*/}
} else {
    for (int x = X1; x<=X2; x++) {/*code*/}
}

Unsuitable because of replicated code, especially if the "//code" section is particularly long

#2: Lots of ternaries

for (int x = X1; x!=X2+(X2<X1?-1:1); x+=(X2<X1?-1:1)) {/*code*/}

While this code works and is concise, it's readability is terrible. Also I've seen in various places that using "not equal to" for your loop constraint is bad practice source

#3: Use a while loop

int x = X1;
while(true) {
    //code
    if (X2<X1) {
        x--;
        if (x<X2) break;
    } else {
        x++;
        if (x>X2) break;
    }
}

This solution seems very long and convoluted to perform such a simple task, in addition, use of "while(true)" is also bad practice source

Upvotes: 0

Views: 3431

Answers (6)

David Dunetz
David Dunetz

Reputation: 43

Why so complicated?

for (int n = 0; n < Count; n++)
{
   int Index = (ascending ? n : Count - 1- n);
}

Upvotes: -1

Zohar Peled
Zohar Peled

Reputation: 82504

A simple solution would be to use one variable for the loop itself, and another variable for the steps:

int length = Math.Abs(x1-x2);
for(int i=0; i <= length; i++)
{
    // step will go either from x1 to x2 or from x2 to x1.
    int step = (x1 < x2) ? x1 + i : x2 + (length-i);
}

Of course, you can wrap the entire loop in a method, so you wouldn't have to repeat the code:

void ForLoopUnknownDirection(int start, int stop, Action<int> action)
{
    int length = Math.Abs(start-stop);
    for(int i=0; i <= length; i++)
    {
        int step = (start < stop) ? start + i : stop + (length-i);
        action(step);
    }
}

This way you can do whatever you want between the numbers while only writing the loop code once.

See a live demo on rextester

Upvotes: 2

Goose
Goose

Reputation: 546

Use a directional increment ( d in the code below )

var d = (x1 > x2) ? -1 : 1;
var i = x1;
while (i != x2)
{
   //
   // insert your code here
   //
   i = i + d;
}

Am I purdy? 😜

Upvotes: 0

Ulf Kristiansen
Ulf Kristiansen

Reputation: 1631

Maybe extract a method like this

private static IEnumerable<int> Step(int start, int end)
{
    if (start < end)
    {
        for (int x = start; x <= end; x++)
            yield return x;
    }
    else
    {
        for (int x = start; x >= end; x--)
            yield return x;
    }
}

Then you can do

foreach (int x in Step(X1, X2))
{
    /*code*/
}

Upvotes: 1

Alexei - check Codidact
Alexei - check Codidact

Reputation: 23088

I think the most readable option is to simple create/extract method from the repeating code (the first proposed version):

void ComputeRenderedStuff(int x) 
{
   // do your computations for x
}

if (X2<X1) 
    for (int x = X1; x>=X2; x--) 
       ComputeRenderedStuff(x);
else
    for (int x = X1; x<=X2; x++)
       ComputeRenderedStuff(x);

Upvotes: 3

dotNET
dotNET

Reputation: 35430

Simply use Math.Min() and Math.Max() to choose lower and upper boundaries.

Something like this:

int MinX = Math.Min(X1, X2);
int MaxX = Math.Max(X1, X2);

for (int x = MinX; x <= MaxX; x++) {
  //code
}

Upvotes: 1

Related Questions