173

I have a list where I want to replace values with None where condition() returns True.

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

For example, if condition checks bool(item%2) should return:

[None, 1, None, 3, None, 5, None, 7, None, 9, None]

What is the most efficient way to do this?

3
  • use the itertools module, it's the most efficient. Commented Jan 29, 2016 at 14:38
  • 2
    For in-place replacement comparisons, take a look at this answer Commented Nov 23, 2016 at 3:12
  • Wow. This question(2009) already has answers from the *future*(2010). So, the important question: How can I time travel? :) Commented Apr 10, 2022 at 3:42

7 Answers 7

248

Build a new list with a list comprehension:

new_items = [x if x % 2 else None for x in items]

You can modify the original list in-place if you want, but it doesn't actually save time:

items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for index, item in enumerate(items):
    if not (item % 2):
        items[index] = None

Here are (Python 3.6.3) timings demonstrating the non-timesave:

In [1]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: for index, item in enumerate(items):
   ...:     if not (item % 2):
   ...:         items[index] = None
   ...:
1.06 µs ± 33.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [2]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: new_items = [x if x % 2 else None for x in items]
   ...:
891 ns ± 13.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

And Python 2.7.6 timings:

In [1]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: for index, item in enumerate(items):
   ...:     if not (item % 2):
   ...:         items[index] = None
   ...: 
1000000 loops, best of 3: 1.27 µs per loop
In [2]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: new_items = [x if x % 2 else None for x in items]
   ...: 
1000000 loops, best of 3: 1.14 µs per loop
Sign up to request clarification or add additional context in comments.

12 Comments

is that the most efficient? doesn't enumerate have to create an iterator and form a tuple, adding overhead? are lists in python arraylists, giving you constant time access?
I think, and I might be wrong, that he meant for a copy of the list to be returned instead of modifying the original in place. But still, +1 for offering the efficient solution when in-place modification is allowed.
@geowa4: Python "lists" are actually arrays. enumerate() will ad a small overhead, but if that's unacceptable the index can be tracked manually. @ak: I don't understand the question. imap() is not an in-place operation.
Thanks for the answers. But isn't there enough of a use case here for python lists to have a replace method? (similar to str.replace). Something that avoids the need for this: def replace(items, a, b): return [b if x == a else x for x in items]
I think you got "easiest to read" and "most efficient" backwards
|
79
ls = [x if (condition) else None for x in ls]

Comments

13

Here's another way:

>>> L = range (11)
>>> map(lambda x: x if x%2 else None, L)
[None, 1, None, 3, None, 5, None, 7, None, 9, None]

1 Comment

@gath: Don't aspire to write one-liners for every purpose. Sometimes, they increase readability or performance, but often they don't. As to hints: Get to know the tools that Python ofters, especially list (and for Python 3 also dict) comprehensions, the ternary operator, anonymous (lambda) functions, and functions like map, zip, filter, reduce, etc.
12

Riffing on a side question asked by the OP in a comment, i.e.:

what if I had a generator that yields the values from range(11) instead of a list. Would it be possible to replace values in the generator?

Sure, it's trivially easy...:

def replaceiniter(it, predicate, replacement=None):
  for item in it:
    if predicate(item): yield replacement
    else: yield item

Just pass any iterable (including the result of calling a generator) as the first arg, the predicate to decide if a value must be replaced as the second arg, and let 'er rip.

For example:

>>> list(replaceiniter(xrange(11), lambda x: x%2))
[0, None, 2, None, 4, None, 6, None, 8, None, 10]

2 Comments

+1 hehe... i want to learn how to write this "one" line nifty python solution... hint pls
@gath, I don't understand your question -- comments are pretty limiting so you should open a new question so you can expand and clarify what is it you're looking for...
2
>>> L = range (11)
>>> [ x if x%2 == 1 else None for x in L ]
[None, 1, None, 3, None, 5, None, 7, None, 9, None]

Comments

1

In case you want to replace values in place, you can update your original list with values from a list comprehension by assigning to the whole slice of the original.

data = [*range(11)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
id_before = id(data)
data[:] = [x if x % 2 else None for x in data]
data
# Out: [None, 1, None, 3, None, 5, None, 7, None, 9, None]
id_before == id(data)  # check if list is still the same
# Out: True

If you have multiple names pointing to the original list, for example you wrote data2=data before changing the list and you skip the slice notation for assigning to data, data will rebind to point to the newly created list while data2 still points to the original unchanged list.

data = [*range(11)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
data2 = data
id_before = id(data)
data = [x if x % 2 else None for x in data]  # no [:] here
data
# Out: [None, 1, None, 3, None, 5, None, 7, None, 9, None]
id_before == id(data)  # check if list is still the same
# Out: False
data2
# Out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Note: This is no recommendation for generally preferring one over the other (changing list in place or not), but behavior you should be aware of.

Comments

-7

This might help...

test_list = [5, 8]
test_list[0] = None
print test_list
#prints [None, 8]

5 Comments

Can you explain some about why you think it might help?
@T-Heron It could be modified to fulfill what the question is asking for
If it needs to be modified, then it isn't an answer to the question that is being asked. Please either make (or explain) the necessary modifications yourself, or delete the answer.
This is hard coding , i don't think best way to do it .
This is not useful in a real usage scenario.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.