Reputation: 463
In the method below, I have assembled a list of tuples, while trying to ensure that none of the values are less than zero. The method below first takes a block and uses it to calculate the coordinates of neighboring blocks in increments of one and then proceeds to remove blocks that have either of their coordinates less than zero. My issue lies in the second stage as it does not remove two coordinates if I input a block with coordinates (0,0): (0,-1) and (-1,0).
The codes is as follows:
def get_block_neighbors(self, block):
neighbor_list = []
neighbor_list.append((block.x,block.y+1))
neighbor_list.append((block.x+1,block.y+1))
neighbor_list.append((block.x+1,block.y))
neighbor_list.append((block.x+1,block.y-1))
neighbor_list.append((block.x,block.y-1))
neighbor_list.append((block.x-1,block.y-1))
neighbor_list.append((block.x-1,block.y))
neighbor_list.append((block.x-1,block.y+1))
for item in neighbor_list: #each tuple item in the list
if item[0] < 0 or item[1] < 0:
print item
neighbor_list.remove(item)
print neighbor_list
get_block_neighbors(Block(Point(0,0),"green"))
for which I get the following output:
(1, -1)
(-1, -1)
(-1, 1)
[(0, 1), (1, 1), (1, 0), (0, -1), (-1, 0)]
Here, the first three lines are printouts of the tuples I would like to remove, while the last one is a list of the tuples that I have assembled. As you can see, the last two tuples have negative values for at least one of their coordinates. Ideally, I would want this:
(1, -1)
(-1, -1)
(-1, 1)
(0, -1)
(-1, 0)
[(0, 1), (1, 1), (1, 0)]
Curiously enough, when I remove/comment out the line neighbor_list.remove(item)
, I get a bit closer to where I need to be in one sense in that the method in its print-out includes the two tuples that I want removed. But of course, the one disadvantage of doing this is that I no longer remove any of the target tuples from this list.
Any help on this would be much appreciated and I really do hope that my oversight wasn't something super obvious. On a side note, I feel like there should be a way to assemble this list while being able to forego a removal stage, which is how I started coding this method before I just settled for the code above. Thanks!
Upvotes: 2
Views: 428
Reputation: 304147
Here is an example, showing some code that looks like it's trying to filter some numbers from a list
>>> L = range(10)
>>> for x in L:
... print x, L
... if x in (4,5,6,7):
... L.remove(x)
...
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6 [0, 1, 2, 3, 5, 6, 7, 8, 9]
8 [0, 1, 2, 3, 5, 7, 8, 9]
9 [0, 1, 2, 3, 5, 7, 8, 9]
Removing items while iterating over the list is generally a bad idea.
Here is a simpler way to get the list of neighbors. It avoids the need for .remove
altogether
def get_block_neighbors(self, block):
x = block.x
y = block.y
xrange = (-1, 0, 1)[x<1:]
yrange = (-1, 0, 1)[y<1:]
return [(x + dx,y + dy) for dx in xrange for dy in yrange if dx or dy]
Upvotes: 0
Reputation: 8492
Much easier all around:
def get_block_neighbors(self, block):
neighbor_list = []
neighbor_list.append((block.x,block.y+1))
neighbor_list.append((block.x+1,block.y+1))
neighbor_list.append((block.x+1,block.y))
if block.y > 0:
neighbor_list.append((block.x+1,block.y-1))
neighbor_list.append((block.x,block.y-1))
if block.x > 0:
neighbor_list.append((block.x-1,block.y-1))
if block.x > 0:
neighbor_list.append((block.x-1,block.y))
neighbor_list.append((block.x-1,block.y+1))
return neighbor_list
Upvotes: 0
Reputation: 104712
The issue is that you're removing items from the list at the same time you're iterating over that list. List iteration happens by index (behind the scenes) so this doesn't work as you'd expect, as some values are skipped when their predecessors are removed.
To avoid this issue, you can iterate over a copy of the list, using a slice:
for item in neighbor_list[:]:
Or, better yet, use a list comprehension to build a new list instead of modifying the old one:
new_list = [(x, y) for x, y in neighbor_list if x >= 0 and y >= 0]
Upvotes: 3
Reputation: 34146
Make a copy of the list first:
for item in neighbor_list[:]: #each tuple item in the list
if item[0] < 0 or item[1] < 0:
print item
neighbor_list.remove(item)
print neighbor_list
Upvotes: 1
Reputation: 236004
The problem lies in the fact that you're removing elements of a list and at the same time you're iterating over it. It's simpler if you just create a new list with the elements removed:
[item for item in neighbor_list if item[0] >= 0 and item[1] >= 0]
Upvotes: 1
Reputation: 25908
You shouldn't remove items from a list you're iterating over. Make a copy of the list first, and iterate over that instead.
Upvotes: 1