Reputation: 18007
I learn how to remove items from a list while iterating from here by:
somelist = [x for x in somelist if determine(x)]
Further, how do I remove a specific index from a list while iterating? For instance,
lists = list() # a list of lists
for idx, line in enumerate(lists):
if idx > 0:
cur_val = line[0] # value of current line
prev_val = lists[idx-1][0] # value of previous line
if prev_val == cur_val :
del lists[idex-1] # IndexError: list index out of range
Upvotes: 1
Views: 195
Reputation: 14360
Basically your attempt is not supported according to any documentation. In general you should not modify a container while iterating unless the documentation explicitly says that you can. It doesn't help if it "seems to work" since you then just exploiting some behavior in the version of the implementation you're using, but that behavior can change without prior notice (breaking your program).
What you need to do is making the modification separate from the iteration. Using list comprehension is one way to achieve this, but basically you're doing the same thing.
There's a few variants, either you make a copy of the data and iterate through that and modify the original. Or you make a copy before iterating the original and modify the copy and then updates the original. There's also the variant where you build the copy during iteration and then update the original.
In addition your example is flawed because you don't take into account that modification affects the proper index in the modified list
. For example if you have the list [1, 1, 2, 2, 3, 3]
, then when you've removed the duplicates 1
s and 2
s and detect duplicate 3
s you have the list [1, 2, 3, 3]
, but when you find the duplicate 3
s you find these at index 4
and 5
, but after the deletion they are at index 2
and 3
instead.
lists = list() # a list of lists
cur = 0 # index in the modified list
for idx, line in list(enumerate(lists)): # make a copy of the original
if idx > 0:
cur_val = line[0] # value of current line
prev_val = lists[idx-1][0] # value of previous line
if prev_val == cur_val:
del lists[cur-1] # IndexError: list index out of range
else:
cur += 1
else:
cur += 1
The solution where you modify a copy instead is basically the same, but the solution where you build the copy during iteration is a bit different.
lists = list() # a list of lists
tmp = []
for idx, line in enumerate(lists): # make a copy of the original
if idx > 0:
cur_val = line[0] # value of current line
prev_val = lists[idx-1][0] # value of previous line
if prev_val != cur_val:
tmp.append(line)
else:
tmp.append( line )
lists[:] = tmp
The last line is where the origin is updated, otherwise the loop works by appending those elements that are to be kept to tmp
(instead of copying all and then remove those that's not to be kept).
Upvotes: 1
Reputation: 12613
You can produce the same thing using list comprehension:
somelist = [i for idx, i in enumerate(lists) if i[0] != lists[idx][0]]
Upvotes: 0