views:

282

answers:

4

I have a two-dimensional array, say

0 0 0 0 0
0 2 3 4 0
0 9 1 5 0
0 8 7 6 0
0 0 0 0 0

And i need to get all the numbers adjacent to 1(2, 3, 4, 5, 6, 7, 8, 9)

Is there a less ugly solution than:

topLeft = array[x-1][y-1]
top  = array[x][y-1]
topRight = array[x+1][y-1]
# etc

Thanks!

+6  A: 

If you're not worried about the order, the cleanest is probably to use a couple of loops:

result = new List<int>(8);
for (dx = -1; dx <= 1; ++dx) {
    for (dy = -1; dy <= 1; ++dy) {
        if (dx != 0 || dy != 0) {
            result.Add(array[x + dx][y + dy]);
        }
    }
}

If the order is important, you can construct a list of all the (dx, dy) in the order you want and iterate over that instead.

As pointed out in the comments, you probably want to add boundary checks. You could do that like this (assuming order doesn't matter):

List<int> result = new List<int>(8);
for (int dx = (x > 0 ? -1 : 0); dx <= (x < max_x ? 1 : 0); ++dx)
{
    for (int dy = (y > 0 ? -1 : 0); dy <= (y < max_y ? 1 : 0); ++dy)
    {
        if (dx != 0 || dy != 0)
        {
            result.Add(array[x + dx][y + dy]);
        }
    }
}
Mark Byers
You need to take into account edge cases. If the specification of (x, y) is (0, 0) then your array indices will be out of bounds. Since this looks like C# code that means you'll get an exception.
Eilon
@Eilon: updated with boundary checks.
Mark Byers
+1  A: 

In C++ this can look like:

vector<int> adj;
for (int i = 0; i < 9; i++)
  if (i != 4) adj.push_back(array[x + i/3 - 1][y + i%3 - 1]);

This is not very clear solution but very short.

sergdev
that solution doesn't generalize at all
twolfe18
@twolfe18 thanks, fixed
sergdev
A: 

I'd probably go for a constant list of dx, dy for each direction, like so:

struct {
    int dx;
    int dy;
} directions[] = {{-1,-1,},{-1,0,},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};

Then you'd iterate over the directions using a simple loop:

for (int i = 0; i < 8; i++) {
    // use x + directions[i].dx;
    // use y + directions[i].dy;
}

You can of course use sizeof(directions) / sizeof(directions[1]) instead of the 8 above.

gooli
+3  A: 

personally, the loops are more ugly than the original.

topLeft  = array[ x - 1 ][ y - 1 ]
top      = array[ x     ][ y - 1 ]
topRight = array[ x + 1 ][ y - 1 ]

midLeft  = array[ x - 1 ][ y     ]
midRight = array[ x + 1 ][ y     ]

botLeft  = array[ x - 1 ][ y + 1 ]
bot      = array[ x     ][ y + 1 ]
botRight = array[ x + 1 ][ y + 1 ]

But without specifying what you want the values for - what you do in the different directions implies whether you want the values in separate variables or not.

For game of life style processing, you usually want to be working on a bitpattern anyway rather than an array of individual values, and you can scan horizontally inspecting only three of the eight cells at a time using accumulators and temporaries. For graphics convolutions, use an existing library with a 3x3 kernel.

The other way of dealing with boundaries is to expand the array by one cell in each direction. This avoids expensive branches in the convolution code.

Pete Kirkham