I'mBehnam
I'mBehnam

Reputation: 7

Remove corresponding list in python

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

Answers (4)

ShadowRanger
ShadowRanger

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

Try it online!

That's the most algorithmically efficient fully in-place solution possible (no temporary lists 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

lunalcni
lunalcni

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

chubercik
chubercik

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

Nir Elbaz
Nir Elbaz

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

Related Questions