9

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;
   }

}
user5648283
  • 5,913
  • 4
  • 22
  • 32
  • 2
    Your code already does `{0,1,...,i-1,i+1,...,radius.length}` for `j` (actually to `radius.length - 1` - but, you can change that by simply writing `j <= radius.length`. What's the problem you're having? – Rob Jan 20 '16 at 06:15
  • 1
    Do you mean `j` from `0` to `radius.length` except for when it equals `i`? – Steve Jan 20 '16 at 06:15
  • Can `++i` ever return `0`? – Eric Jan 20 '16 at 06:22
  • I've edited the question to make it more explicit, I think some people miss the fact you don't want `i` in the range. – Kobi Jan 20 '16 at 06:36

5 Answers5

13
  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
        }    
    
Community
  • 1
  • 1
Ian
  • 30,182
  • 19
  • 69
  • 107
  • I've used `continue` and thought I'd never need to, but you've really sold me here ... – user5648283 Jan 20 '16 at 06:18
  • @user5648283 why have you thought that you'd never need to? ;) it is nice. – Ian Jan 20 '16 at 06:20
  • Have a look into yield return maybe? https://msdn.microsoft.com/en-us/library/65zzykke(v=vs.90).aspx – Pomadomaphin Jan 20 '16 at 06:22
  • @user5648283 ok, I improved my answer... ;) please take a look – Ian Jan 20 '16 at 06:34
  • @user5648283 You really don't need to. If it were me, I'd take the extra level of indentation and go with "if (i != j) { }" – Millie Smith Jan 20 '16 at 06:37
  • @MillieSmith that's surely another alternative. – Ian Jan 20 '16 at 06:38
  • The second one is very confusing. – Kobi Jan 20 '16 at 06:39
  • @Kobi ah, yes. It is meant to be compact... rather than readable... :) Maybe I should put quotation mark on the word "little"... – Ian Jan 20 '16 at 06:40
  • 1
    IMO the option (1) is the best, since it doesn't change the indentation, is readable and represents exactly " want **j** to go through the range 0-radius.length, but **skip over i**". I disagree with the last note that OP solution is hard to beat - it doesn't need to be beat because it's incorrect (when i = 0 and j = 0). – Ivan Stoev Jan 20 '16 at 10:36
  • @IvanStoev regarding (1), I totally agree! :) and yes, in my opinion, the code is best when it best represents what it actually means. Regarding the last note, it would be best to read it with respect to my small capacity. ;) but regarding the mistake it has, I just noticed it! – Ian Jan 20 '16 at 11:40
6

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
2

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
    }
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Andrey Tretyak
  • 3,043
  • 2
  • 20
  • 33
  • 1
    Whenever you find yourself using `SelectMany`, consider whether a [query expression approach might be cleaner](http://stackoverflow.com/a/34893628/71059) – AakashM Jan 20 '16 at 10:17
  • @AakashM you totally right, in this case query approach looks cleaner, but for me extension methods much easier to understand and write – Andrey Tretyak Jan 20 '16 at 13:32
2

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

flipside
  • 81
  • 1
  • 5
0

The orthodox solution as demonstrated above is the continue statement, but if the if keyword is taboo at your workplace, an alternative is to split the loop in two:

for ( int j = 0; j < i; ++j ) 
{
}  
for ( int j = i+1; j < radius.Length; ++j ) 
{
} 
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465