Avoid if statements for checking neighboring indexes in a 2D array

Timon Gehr via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Jul 16 10:10:49 PDT 2017


On 16.07.2017 18:55, Timon Gehr wrote:
> On 16.07.2017 12:37, kerdemdemir wrote:
>> My goal is to find connected components in a 2D array for example 
>> finding connected '*'
>> chars below.
>>
>>        x x x x x x
>>        x x x x x x
>>        x x * * x x
>>        x x * * x x
>>        x x x * * x
>>        * x x x x x
>>
>>
>> There are two connected '*' group in this example. First group is 
>> composes of six '*' located closer to middle and the second group 
>> composes only one '*' char located in the left bottom corner.
>>
>> Do to this I generally implement a recursive algorithm which repeat 
>> calling the same function by checking all neighbors around the current 
>> index. I generally end up with something like :
>>
>> void foo( int row, int col)
>> {
>>      //Do something here like caching the index
>>
>>      if ( twoDimensionData[row - 1][col] == '*')
>>         foo(row- 1, col);
>>      else if ( twoDimensionData[row + 1][col] == '*')
>>         foo(row+ 1, col);
>>      else if ( twoDimensionData[row - 1 ][col - 1] == '*')
>>         foo(row - 1, col - 1);
>>
>> //..... I need 5 more of this bad boys I mean if checks
>> }
>> ...
> 
> It is wrong to explore in only one direction, so I assume you do not 
> mean "else".
> 
>> Is there any better way to achieve this
> foreach(i;row-1..row+2){
>      foreach(j;col-1..col+2){
>          if(i==row && j==col) continue;
>          if(twoDimensionData[i][j] == '*')
>              foo(row,col);
>      }
> }
> 
>> with cool std functions like enumerate or iota without needing to 
>> write eight if checks?
> 
> cartesianProduct(iota(row-1,row+2),iota(col-1,col+2))
>      .filter!(a=>(a[0]!=row||a[1]!=col))
>      .filter!(a=>twoDimensionData[a[0]][a[1]]=='*')
>      .each!(a=>foo(a.expand));
> 
> (You can usually drop the first filter because "doing something" will 
> usually involve checking if the node has been visited and returning or 
> else marking the node as visited.)

Ivan's example in this style:

import std.stdio, std.range, std.algorithm, std.array;
char[][] arr;
int componentSize(size_t row, size_t col){
     if(row>=arr.length||col>=arr[row].length||arr[row][col]!='*')
         return 0;
     arr[row][col]='x';
     return 1+cartesianProduct(iota(row-1,row+2),iota(col-1,col+2))
         .map!(a=>componentSize(a.expand)).sum;
}
void main (){
     arr=["xxxxxx*",
          "xxxx*xx",
          "xx**xxx",
          "xx**x**",
          "xxxxxxx"].map!dup.array;
     cartesianProduct(iota(arr.length),iota(arr[0].length))
         .filter!(a=>arr[a[0]][a[1]]=='*')
         .each!(a=>writeln(componentSize(a.expand)));
}

(This works even if there are * at the border.)


More information about the Digitalmars-d-learn mailing list