2

and b. I want to delete all the numbers in list b where there is a '0' as well as the corresponding numbers in a that share the same index with the zeroes in b. This is my code:

a = [ 1 , 23 , 3 , 45 , 5 , 63 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15] b = [ 8 , 0 , 0 , 7 , 0 , 9 , 3 , 2 , 4 , 13 , 25 , 45 , 34 , 25 , 11] indexzeroes = [i for i, j in enumerate(b) if j == 0] for i in indexzeroes: b.pop(i) a.pop(i) print a print b 

However I get the wrong updated lists for a and b. I've identified the reason as being that in the 'for loop' I've changed the list structure each time I 'pop' an item so that the indexes with the remaining zeroes in change also.

This seems so convoluted for such a ostensibly simple problem. Can anyone help?

4 Answers 4

5

The reason is because when you pop the ith index from a or b, all the elements shift once to the left. You can solve this by popping the items in reverse (since the indices should be in sorted order):

for i in reversed(indexzeroes): a.pop(i) b.pop(i) 

Wit that said, this is probably somewhat inefficient for large lists (worst case O(n^2)). You're better off using a set which will give you an O(n) algorithm at the expense of a little extra memory:

indexzeroes = {i for i, j in enumerate(b) if j == 0} a = [x for i, x in enumerate(a) if i not in indexzeros] b = [x for i, x in enumerate(b) if i not in indexzeros] 
Sign up to request clarification or add additional context in comments.

Comments

1

You can remove if you start from the end of the lists:

for ind in range(len(a) - 1, -1, -1): if b[ind] == 0: del a[ind] del b[ind] 

Output:

[1, 45, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15] [8, 7, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11] 

Starting from the ends works as the lists are getting smaller so any element we have not yet seen will still be at the same index as the index is smaller than anything we have seen so far. You should also only really use list.pop if you want to use the element it returns, if you just want to remove then list.remove or del as above would be the best.

You can also do it in O(n) time with using a set, by creating new lists:

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15] b = [8, 0, 0, 7, 0, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11] from itertools import izip def remove_(i,l1,l2): c,d = [], [] for j, k in izip(l1, l2): if k != i: c.append(j),d.append(k) return c, d a, b= remove_(0, a, b) print(a,b) 

Comments

1

As an alternative approach, you could use a list comprehension as follows:

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15] b = [8, 0 , 0, 7 , 0, 9 , 3, 2, 4, 13, 25, 45, 34, 25, 11] a_out = [] b_out = [] [(a_out.append(v1), b_out.append(v2)) for v1, v2 in zip(a,b) if v2] print a_out print b_out 

This would display the following:

[1, 45, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15] [8, 7, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11] 

If the lists are large, you could also switch to using itertools.izip instead of zip.

Or as suggested, using a vanilla for-loop:

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15] b = [8, 0 , 0, 7 , 0, 9 , 3, 2, 4, 13, 25, 45, 34, 25, 11] a_out = [] b_out = [] for v1, v2 in zip(a,b): if v2: a_out.append(v1) b_out.append(v2) print a_out print b_out 

1 Comment

Generally speaking, It's not considered good style to use a list comprehension for it's side effects (in this case calling append). Usually in those cases a vanilla loop is preferred.
0

An alternative way, you could use the filter function. # It makes the code one-liner :)
Here is the code for you..

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15] b = [8, 0, 0, 7, 0, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11] print(list(filter(lambda x: x[0] and x[1], zip(a, b)))) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.