Reputation: 7
I have a list in this format : [(10,2),(5,3),(15,5),(7,7),(6,1),(18,4),(3,1)]
and another list like this : [1, 1, 1, 0, 1, 1, 0]
now I want to remove all items from first one list where second list item value is 1.
list1 = [(10,2),(5,3),(15,5),(7,7),(6,1),(18,4),(3,1)]
list2 = [1, 1, 1, 0, 1, 1, 0]
Final result should be: [(7,7), (3,1)]
How can I do this?
Upvotes: 0
Views: 66
Reputation: 155363
You want the inverse of itertools.compress
. Which can be built with built-in functions as:
from itertools import compress
from operator import not_
list3 = list(compress(list1, map(not_, list2)))
and will be the most efficient solution for large inputs (especially when you omit the list
wrapping to loop over the compress
object directly)
But that's getting a little dense, meaning-wise, so you may just want to write it as the listcomp that compress
is equivalent to, tweaking the test to invert it:
list3 = [val for val, selector in zip(list1, list2) if not selector]
Both cases can be tweaked to update list1
"in-place" with a simple slice assignment:
list1[:] = compress(list1, map(not_, list2))
# or
list1[:] = [val for val, selector in zip(list1, list2) if not selector]
If for some reason you must remove the items directly in-place (no creating temporary structures of the new data that then replaces the old data within list1
), you can iterate over the inputs in tandem (with zip
as before) and manually track the number of elements kept so far, allowing you to keep the work O(n)
by not performing repeated copy-down operations, deferring the actual deletion of elements to the end so the list
is only actually resized (at most) once:
kept_count = 0
for val, selector in zip(list1, list2):
if not selector:
list1[kept_count] = val # Move an alias to the element to its new position
kept_count +=1 # We kept another element, track that
del list1[kept_count:] # Bulk delete all elements after those kept
That's the most algorithmically efficient fully in-place solution possible (no temporary list
s or other O(n)
storage-requirements involved), but it's the most complex and it's likely to be slower on the CPython reference interpreter than the deferred-in-place solutions, simply because it does more work at the Python layer without direct support from the bytecode interpreter (simple integer math is one of those things where CPython tends to have the worse overhead to productive work done ratio, at least prior to 3.11 when they added some optimizations for it). On the bright side, it's extremely good on memory, as the piecemeal copy down replaces earlier elements with aliases of later elements, potentially clearing memory as it goes (only meaningful if the elements being replaced are huge and not aliased elsewhere, but it's still something). You could make it free the memory even faster by None
-ing out as you go:
kept_count = 0
for i, (val, selector) in enumerate(zip(list1, list2)): # Track current index
if not selector:
list1[kept_count] = val
kept_count +=1
else:
list1[i] = None # Eagerly clear references to elements we're not keeping
del list1[kept_count:]
but it's unlikely to help; you end up doing more work (to track index and explicitly None
out stuff you're not keeping), just to free memory a little faster.
For performance comparisons, some IPython microbenchmarks, based on the following versions (all run on CPython 3.10.5, x86-64, on Linux):
from itertools import compress
from operator import not_
def cutdown1(lst, selectors):
lst[:] = compress(lst, map(not_, selectors))
def cutdown2(lst, selectors):
lst[:] = [val for val, selector in zip(lst, selectors) if not selector]
def cutdown3(lst, selectors):
kept_count = 0
for val, selector in zip(lst, selectors):
if not selector:
lst[kept_count] = val
kept_count +=1
del lst[kept_count:]
def cutdown4(lst, selectors):
kept_count = 0
for i, (val, selector) in enumerate(zip(lst, selectors)):
if not selector:
lst[kept_count] = val
kept_count +=1
else:
lst[i] = None
del lst[kept_count:]
Timings:
>>> %%timeit list1 = [(1,2), (3,4), (5,6), (7,8), (9,10)] * 10; list2 = (1,1,0,1,0) * 10; c = cutdown1
... c(list1.copy(), list2)
...
1.36 µs ± 47.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %%timeit list1 = [(1,2), (3,4), (5,6), (7,8), (9,10)] * 10; list2 = (1,1,0,1,0) * 10; c = cutdown2
... c(list1.copy(), list2)
...
1.79 µs ± 80.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %%timeit list1 = [(1,2), (3,4), (5,6), (7,8), (9,10)] * 10; list2 = (1,1,0,1,0) * 10; c = cutdown3
... c(list1.copy(), list2)
...
2.09 µs ± 5.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
>>> %%timeit list1 = [(1,2), (3,4), (5,6), (7,8), (9,10)] * 10; list2 = (1,1,0,1,0) * 10; c = cutdown4
... c(list1.copy(), list2)
...
3.25 µs ± 53.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
As expected, using tools that push all the work to the C layer in CPython (compress
, map
+not_
, slice assignment) is the fastest, those that benefit from dedicated bytecodes (listcomp) and some C level improvements (slice assignment from list
to list
) are a little slower, and those that do almost all the work at the Python layer are the slowest (and the more they do, the slower they go).
Upvotes: 2
Reputation: 142
Use zip() to treat second list as flag
>>> list1 = [(10,2), (5,3), (15,5), (7,7), (6,1), (18,4), (3,1)]
>>> list2 = [1, 1, 1, 0, 1, 1, 0]
>>> rst = [item for item, flag in zip(list1, list2) if not flag]
>>> rst
[(7, 7), (3, 1)]
Upvotes: 2
Reputation: 765
list1 = [(10, 2), (5, 3), (15, 5), (7, 7), (6, 1), (18, 4), (3, 1)]
list2 = [1, 1, 1, 0, 1, 1, 0]
removed = 0
for i in range(len(list2)):
if list2[i]:
del list1[i - removed]
removed += 1
print(list1)
Result:
[(7, 7), (3, 1)]
Upvotes: 1
Reputation: 616
list1 = [(10,2),(5,3),(15,5),(7,7),(6,1),(18,4),(3,1)]
list2 = [1, 1, 1, 0, 1, 1, 0]
templist=[]
if len(list1)==len(list2):
i=0
for index in list2:
if index==0:
templist.append(list1[i])
i+=1
list1=templist
Upvotes: 0