6

Given:

a = [[1,2],[3,4],[5,6],[7,8]]
b = 3

I would like to remove an item of a that has b as it's first item. So in this case we would remove [3,4] to give:

a = [[1,2],[5,6],[7,8]]

My current code is:

if b in [i[0] for i in a]:
    pos = [i[0] for i in a].index(b)
       del a[pos]

This works but it is slow. What would a better way to do this be?

EDIT: I've not tested performance before so I may be doing this wrong but I get this:

def fun1():
    lst = [[x, 2*x] for x in range(1000000)]
    lst = [x for x in lst if x[0] != 500]
    return lst

def fun2():
    lst = [[x, 2*x] for x in range(1000000)]
    for i in reversed(range(len(lst))):
        if lst[i][0] == 500:
            del lst[i]
    return lst

cProfile.runctx('fun1()', None, locals())
        6 function calls in 0.460 seconds

cProfile.runctx('fun2()', None, locals())
        6 function calls in 0.502 seconds
cs95
  • 379,657
  • 97
  • 704
  • 746
AndrewK
  • 271
  • 2
  • 16
  • How large is "fairly large"? Large enough to be worth considering rearranging your code so you have a numpy array instead of a list? Or using PyPy instead of CPython? Or building a quick C extension that does Coldspeed's solution with Cython? – abarnert Apr 05 '18 at 07:39
  • @abarnert That's above my head at the moment tbh but it isn't that large. – AndrewK Apr 05 '18 at 08:08

4 Answers4

11

Reverse delete a, modifying it in-place:

for i in reversed(range(len(a))):
    if a[i][0] == 3:
        del a[i]

An in-place modification means that this is more efficient, since it does not create a new list (as a list comprehension would).


Since OP requests a performant solution, here's a timeit comparison between the two top voted answers here.

Setup -

a = np.random.choice(4, (100000, 2)).tolist()

print(a[:5])
[[2, 1], [2, 2], [3, 2], [3, 3], [3, 1]]

List comprehension -

%timeit [x for x in a if x[0] != b]
11.1 ms ± 685 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Reverse delete -

%%timeit
for i in reversed(range(len(a))):
    if a[i][0] == 3:
        del a[i]

10.1 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

They're really close, but reverse delete has a 1UP on performance because it doesn't have to generate a new list in memory, as the list comprehension would.

cs95
  • 379,657
  • 97
  • 704
  • 746
  • What about `a[:] = [x for x in a if x[0] != b]` if you don't want to build a new list? – Matthias Apr 05 '18 at 07:40
  • @Matthias Note that the `[x for x in a if x[0] != b]` creates a new list and just reassigns it back to `a`. – cs95 Apr 05 '18 at 07:40
  • @cᴏʟᴅsᴘᴇᴇᴅ: Took me some time, but the more I look at your solution the more I like the idea. – Matthias Apr 05 '18 at 07:41
  • 1
    @Matthias The reason I didn't write a list comprehension answer is because this is an awesome idiom that not enough developers know about/seem to use, so thought I'd shine some light on it. Credits to wim for introducing me to this one a long time back. – cs95 Apr 05 '18 at 07:43
  • I compared @KeyurPotdar and coldspeed, I timed your two solutions but I found the list comprehension to be slightly faster. I have edited my post with my results. I am not sure why they differ from yours. – AndrewK Apr 05 '18 at 08:05
  • 1
    @AndrewK It depends on many factors, size being one of them, python version being another. List comprehensions are optimised to the point that they're actually slightly faster than loops themselves. Reverse delete really shines in its memory efficiency. Another thing, this was timed on python3.6, your mileage may vary. – cs95 Apr 05 '18 at 08:07
7

You can use a list comprehension:

>>> a = [[1,2],[3,4],[5,6],[7,8]]
>>> b = 3
>>> a = [x for x in a if x[0] != b]
>>> a
[[1, 2], [5, 6], [7, 8]]
Keyur Potdar
  • 7,158
  • 6
  • 25
  • 40
  • 1
    There's a space-time tradeoff here - I'd find this answer preferable for large lists too, since CS's in-place deletes on Python's array-backed lists mean moving every element from the deleted one to the end down one, I'd expect it to take much longer if the list is long and there are many deletes. – Russia Must Remove Putin Apr 06 '18 at 02:27
1
for i in a[:-1]:
    if i[0]==b:
        a.remove(i)

What about this?

The output is

[[1, 2], [5, 6], [7, 8]]

Pabasara Ranathunga
  • 160
  • 1
  • 2
  • 18
1

If your list are small then you are also try filter ,

a = [[1,2],[3,4],[5,6],[7,8]]
b = 3

print(list(filter(lambda x:x[0]!=b,a)))

output:

[[1, 2], [5, 6], [7, 8]]
Aaditya Ura
  • 12,007
  • 7
  • 50
  • 88