setarkos
setarkos

Reputation: 21

Delete item from list upon a condition

I have a list of lists of tuples of integers.

ls = [[(a_1, a_2), (b_1, b_2)], [(c_1, c_2), (d_1, d_2), (e_1, e_2)], ...]

And I need to delete every item of ls that contains a tuple whose second entry is equal to a predetermined integer.

I tried this:

  for item in ls:
    for tpl in item:
      if tpl[1] == m:
        ls.remove(item)

But for some reason, this only removes a few of the list items but not all containing a tuple with second entry = m.

Upvotes: 1

Views: 123

Answers (5)

Ulf Aslak
Ulf Aslak

Reputation: 8608

Code sucks and we need less of it - here's as sparse as it gets.

[l for l in ls if m not in [i[1] for i in l]]

Upvotes: 1

Mad Physicist
Mad Physicist

Reputation: 114300

Python's list iterator is lazy. This means that when you remove an item from the list, it will skip the next item. For example, say you want to remove all ones from the following list:

[1, 1, 2]

Your for loop starts at index 0:

[1, 1, 2]
 ^

It removes the element and moves on:

[1, 2]
    ^

This example is just to help illustrate the issue. One simple workaround is to loop backwards using the index:

for ind in range(len(ls)-1, -1, -1):
    item = ls[ind]
    for tpl in item:
        if tpl[1] == m:
            del ls[ind]
            break # You can end the inner loop immediately in this case

Another way is to make a copy of the list to iterate over, but remove from the original:

for item in ls[:]:
    for tpl in item:
        if tpl[1] == m:
            ls.remove(item)
            break

The last approach can be simplified into creating an output list that contains only the elements that you want. This is easiest to do with a list comprehension. See @AlexeySmirnov 's answer for the best way to do that.

Upvotes: 0

Alexey Smirnov
Alexey Smirnov

Reputation: 2613

Removing an itme from list is not a good idea while iterating though it.

Try that (if where are talking Python here)

ls = [[('a_1', 'a_2'), ('b_1', 'b_2')], [('c_1', 'c_2'), ('d_1', 'd_2'), ('e_1', 'e_2')]]

m='c_2'
print [ x for x in ls if not [ y for y in x if y[1]==m ]]

Upvotes: 0

Akshat Mahajan
Akshat Mahajan

Reputation: 9846

Use a list comprehension:

ls = [item for item in ls if all(tuple[1] != m for tuple in item)]

Or use a filter:

ls = filter(lambda item: all(tuple[1] != m for tuple in item),ls)

Upvotes: 3

Nix G-D
Nix G-D

Reputation: 787

The best way to filter a list in python is to use a list comprehension:

filtered = [item for item in ls if not(contains_m(item))]

And then all you need is a function that can tell if an item contains m, for example:

def contains_m(item):
     return any([tpl[1] == m for tpl in item])

Upvotes: 0

Related Questions