Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does C# have a nice way of iterating through every integer in a range, minus 1 of them?

The situation I have is like

// radius is an int[]
for ( int i = 0; i < radius.length; ++i )
{
   for ( int j = 0; j < radius.length; ++j ) 
   {
       // do some stuff
   }  
}

Except I actually want j to go through the range 0-radius.length, but skip over i:

{0,1,..., i-1, i+1, ..., radius.length}

I'm wondering if there's a way to do this that is compact, elegant, efficient, readable, and maybe even correct.

How I planned to do it was

for ( int i = 0; i < radius.length; ++i )
{
   for ( int j = 0; j < radius.length; )
   {
       // do some stuff
       j += j != i ? 1 : 2;
   }

}
like image 834
user5648283 Avatar asked Jan 20 '16 06:01

user5648283


4 Answers

  1. Perhaps you may consider of using continue:

    // radius is an int[]
    for (int i = 0; i < radius.length; ++i)    
       for (int j = 0; j < radius.length; ++j) 
       {
           if (i == j)
               continue;
           // do some stuff
       }  
    

    That is one of the simplest I could think of. It is representing exactly what you want as an independent block.

  2. If you want to be more "compact" (with the cost of "little" readability), you may consider of using multi-variables single loop instead of single variable nested loops:

    int r = radius.Length;
    for (i = 0, j = 0; i < r - 1 || j < r; j++) {
        i += j == r ? 1 : 0;
        j %= r;
        if (i == j)
            continue;
        //do stuff
    }
    
  3. Or another alternative without using continue would be (suggested by Millie Smith):

    for (int i = 0; i < radius.length; ++i)    
       for (int j = 0; j < radius.length; ++j)        
           if (i != j) {
               // do some stuff       
           }
    

    This way, at least you could get rid of all the curly brackets but on if.

Note: But I personally think that your solution (using j += j != i ? 1 : 2;) is already quite compact, elegant, efficient, readable! It is quite hard to "beat"! ;)

Edit:

"quite hard to beat..." except that the original solution contains a single error (as identified by Ivan Stoev) when i == 0 && j == 0.

  1. To make it right, a modified solution would be something like this:

    for ( int i = 0; i < radius.length; ++i )   
        for ( int j = 0; j < radius.length; )
        {
            // do some stuff
            j += j != i || i + j == 0 ? 1 : 2; //i + j == 0 condition added
        }    
    
like image 83
Ian Avatar answered Oct 19 '22 06:10

Ian


How about this?

var query = 
    from i in Enumerable.Range(0, radius.Length)
    from j in Enumerable.Range(0, radius.Length)
    where i != j
    select new { i, j };

foreach (var x in query)
{
    /* Do stuff with x.i && x.j */
}

I think that's fairly neat.

like image 24
Enigmativity Avatar answered Oct 19 '22 06:10

Enigmativity


You could build it into the iterator:

        for (int i = 0; i < radius.length; i++)
        {
            for (int j = i == 0 ? 1 : 0; j < radius.length; j += j == i - 1 ? 2 : 1)
            {
                //-Do something
            }
        }

Edit:

Updated inner loop initializer to: int j = i == 0 ? 1 : 0

like image 22
flipside Avatar answered Oct 19 '22 08:10

flipside


Another variant is using foreach loop and Range method:

foreach(var i in Enumerable.Range(0,radius.length))
{
   foreach(var j in Enumerable.Range(0,radius.length).Where(r => r != i))
   {
     // do some stuff
   }
}

Or you can even put it in one foreach loop, but expression will be not very compact:

    var range = Enumerable.Range(0, radius.length)
               .SelectMany(i => Enumerable.Range(0, Enumerable.Range(0, radius.length)
                         .Where(r => r != i))
                         .Select(j => new {i, j}));

    foreach (var value in range)
    {
        value.i //i value
        value.j //j value
    }
like image 2
Andrey Tretyak Avatar answered Oct 19 '22 07:10

Andrey Tretyak