Joe
Joe

Reputation: 1388

Python List remove() removing more than 1 element

So I have a python bot where there are 2 teams, and there is a scramble call that is supposed to scramble the teams. The two teams are each stored in a list, and I have to scramble them. (now before you say use random shuffle, that won't work here)

Each list is a list of dictionaries - each element is a dictionary object containing info about a user. (e.g. user nick, user class, user ban status, user win %, etc)

I can't change that - that is out of my control. I came up with the solution below:

for i in switchingClassList: #switchingClassList is already filled with random classes that will be switched
        playerList = []
        for j in teamA:
            if j['class'][0] == i: #if the user's class matches one in switchingClassList, that user will be part of the scramble
                playerList.append(j)
                teamA.remove(j)
        for j in teamB:
            if j['class'][0] == i:
                playerList.append(j)
                teamB.remove(j) 
#there is more after, but that part works

So this thing works... sort of. In a team, there are 5 unique classes, all but 1 of which are taken up by 1 player. That last class is NOT taken by 1 player but by TWO players per team.

If SwitchClassList contains that class that has 2 players per class, this thing breaks.

I traced the error to this line:

teamA.remove(j)

It turns out that if the class with two players in it is chosen, this line removes BOTH players from the teamA list (and in turn teamB list). But... I thought list's remove in python removed the FIRST element only. Am I misunderstanding the function? What is a potential fix to this?

Upvotes: 0

Views: 534

Answers (1)

Tim Peters
Tim Peters

Reputation: 70592

OK - the problem seemed to be caused by mutating the teamA and teamB lists while iterating over them. That's rarely ;-) a good idea.

>>> alist = range(10)
>>> alist
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i, x in enumerate(alist):
...     if i < 5:
...         alist.remove(x)
...
>>> alist
[1, 3, 5, 7, 9]

Huh? LOL ;-) CPython actually defines what happens here, but it's never a good idea to rely on it.

Iterating over a copy of the list is often the quickest way to fix it:

>>> alist = range(10)
>>> for i, x in enumerate(alist[:]):
...     if i < 5:
...         alist.remove(x)
...
>>> alist
[5, 6, 7, 8, 9]

Upvotes: 4

Related Questions