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