Reputation: 29
Im trying to make a set based in another set, and exclude only one item... (do a for loop inside another for loop with an object that is inside a set, but not iterate with itself on the second loop)
Code:
for animal in self.animals:
self.exclude_animal = set((animal,))
self.new_animals = set(self.animals)
self.new_animals.discard(self.exclude_animal) # parameters for a set needs to be a set?
for other_animal in (self.new_animals):
if animal.pos[0] == other_animal.pos[0]:
if animal.pos[1] == other_animal.pos[1]:
self.animals_to_die.add(animal)
print other_animal
print animal
self.animals_to_die.add(other_animal)
Point is, my print statement returns the object id(x)
, so I know that they are the same object, but they should not be, I discard it on that set new_animals
set.
Any insight in why this doesn't exclude the item?
Upvotes: 2
Views: 2760
Reputation: 95712
As you mark both animals to die, you don't need to compare A to B and also B to A (which your current code does). You can ensure you get only unique pairs of animals by using itertools.combinations()
:
for animal, other_animal in itertools.combinations(self.animals, 2):
if animal.pos==other_animal.pos:
self.animals_to_die.update([animal, other_animal])
Just for fun, I'll point out you can even do it as a single expression (though I think it reads better as an explicit loop):
self.animals_to_die.update(sum(((animal,other_animal)
for animal,other_animal in itertools.combinations(self.animals, 2)
if animal.pos == other_animal.pos), ()))
For clarity, itertools.combinations()
gives you all the unique combinations of its input. The second argument controls how many elements are selected each time:
>>> list(itertools.combinations(["A", "B", "C", "D"], 2))
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
>>> list(itertools.combinations(["A", "B", "C", "D"], 3))
[('A', 'B', 'C'), ('A', 'B', 'D'), ('A', 'C', 'D'), ('B', 'C', 'D')]
That works well in this case as the code given appears to be symmetric (two animals on the same location mutually annihilate each other). If it had been asymmetric (one kills the other) then you would have to remember to test both ways round on each iteration.
Upvotes: 1
Reputation: 1123420
set.discard()
removes one item from the set, but you pass in a whole set object.
You need to remove the element itself, not another set with the element inside:
self.new_animals.discard(animal)
Demo:
>>> someset = {1, 2, 3}
>>> someset.discard({1})
>>> someset.discard(2)
>>> someset
set([1, 3])
Note how 2
was removed, but 1
remained in the set.
It would be easier to just loop over the set difference here:
for animal in self.animals:
for other_animal in set(self.animals).difference([animal]):
if animal.pos == other_animal.pos:
self.animals_to_die.add(animal)
print other_animal
print animal
self.animals_to_die.add(other_animal)
(where I assume that .pos
is a tuple of two values, you can just test for direct equality here).
You don't need to store new_animals
on self
all the time; using local names suffices and is not even needed here.
Upvotes: 6