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?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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
Method 2
ls = [x if (condition) else None for x in ls]
Method 3
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]
Method 4
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]
Method 5
>>> 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]
Method 6
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.
Method 7
This might help…
test_list = [5, 8] test_list[0] = None print test_list #prints [None, 8]
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0