4

I have a list of lists as follows :

g = [
    ["a","?","?","?","?","?","?"],
    ["?","b","?","?","?","?","?"],
    ["?","?","?","?","?","?","?"]
]

I want to iterate through this list and delete the list which contains all "?", which is the last list in this list of lists. I've tried pop() , del and several other operation but nothing seems to be working so far.

Here's what I've written for this part :

for x in g:
    if x.count("?") == 6:
        g.pop(g.index(x))

It doesn't remove the list but removes one "?" from the last list. Can anyone please guide me here.

  • 2
    I think `x.pop(g.index(x))` should be `g.pop(g.index(x))` - `g` is your list of lists, `x` is one of the lists of characters. – The Zach Man Jan 11 '19 at 03:43
  • Also consider 6 versus 7. – TrebledJ Jan 11 '19 at 03:43
  • Sorry my bad, that's actually g.pop(g.index(x)), typo error. Corrected it now, would appreciate your response on this. – Shekhar Tanwar Jan 11 '19 at 03:45
  • Just use a list comp to create a new list: `[l for l in x if any(el != "?" for el in l)]` – pault Jan 11 '19 at 03:48
  • Try changing your test to `if all(c=='?' for c in x):`, more robust over time than testing against a number of entries. – PaulMcG Jan 11 '19 at 03:52
  • 1
    The problem with `pop`ing in-place as you iterate is that you'll be skipping the next item in the list; how is this? Consider, for instance, that the current iteration is at index 0, and the `if` condition turns out to be `true`, that means you `pop` the item at that index 0, where will the iteration be standing??? The element at index 1 will now be the new index 0; if that's so, since the next iteration will be at index 1, which is, after the `pop` the element that was at index 2 before the `pop`, the element at index 0 after the pop will not be checked. – John Mutuma Jan 11 '19 at 04:25

6 Answers6

4

You should leverage set here:

In [152]: X = [["a","?","?","?","?","?","?"],["?","b","?","?","?","?","?"],["?","?","?","?","?","?","?"]]

In [153]: [l for l in X if set(l) != {"?"}]
Out[153]: [['a', '?', '?', '?', '?', '?', '?'], ['?', 'b', '?', '?', '?', '?', '?']]

set(l) gets the unique values of the list and makes a set out of it, comparing the resulting set with {"?"} would suffice as you want to drop the list with all ?s.

heemayl
  • 39,294
  • 7
  • 70
  • 76
2

Try this list comprehension:

X = [["a","?","?","?","?","?","?"],["?","b","?","?","?","?","?"],["?","?","?","?","?","?","?"]]

print([l for l in X if l.count("?") == len(l)])

Output:

[['a', '?', '?', '?', '?', '?', '?'], ['?', 'b', '?', '?', '?', '?', '?']]

As an added bonus, it is 2x faster than @heemayl's answer.

iz_
  • 15,923
  • 3
  • 25
  • 40
  • Why is this 2x faster? `count()` and `set()` should have the same complexity. For speed, `any` or `all` would be the fastest (because of short circuiting) – pault Jan 11 '19 at 14:47
  • @pault `count` has an efficient C implementation, and `len` is stored internally, making this method quite fast. Using `timeit` trials, this method is consistently faster. See https://stackoverflow.com/a/3844948 for more details. – iz_ Jan 11 '19 at 16:34
1

May be this would help!

[x for i,x in enumerate(X) if ''.join(x).replace('?','')]

output:

[['a', '?', '?', '?', '?', '?', '?'], ['?', 'b', '?', '?', '?', '?', '?']]
Community
  • 1
  • 1
Venkatachalam
  • 16,288
  • 9
  • 49
  • 77
1

In case list comprehensions are not very clear as in the answers you have received, an alternative can have this form;

l = [['a', '?'], ['?', '?']]

result = []
for i in l:
    if ('?' in i) and (set(i) == {'?'}):
        continue
    result.append(i)
John Mutuma
  • 3,150
  • 2
  • 18
  • 31
0

Let's use Lists comprehensions to solve this -

all([True, True, True)] 
   True
all(True, True, False)
   False

Thus we compare each element of sub-lists to '?' and in case they are '?', then we return True and finally apply all() function to the sub-lists.

g_out = [i for i in g if all([j=='?' for j in i]) == False]
print(g_out)
    [['a', '?', '?', '?', '?', '?', '?'], ['?', 'b', '?', '?', '?', '?', '?']]
cph_sto
  • 7,189
  • 12
  • 42
  • 78
0

You can use filter with lambda functions as well:

func = lambda x: x.count("?") != len(x)
new_g = list(filter(func, g))
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73