'Replace None in list with leftmost non none value
Given
a = [None,1,2,3,None,4,None,None]
I'd like
a = [None,1,2,3,3,4,4,4]
Currently I have brute forced it with:
def replaceNoneWithLeftmost(val):
last = None
ret = []
for x in val:
if x is not None:
ret.append(x)
last = x
else:
ret.append(last)
return ret
Finally, I'd like to get to
a = [1,1,2,3,3,4,4,4]
by running this right to left. Currently I have
def replaceNoneWithRightmost(val):
return replaceNoneWithLeftmost(val[::-1])[::-1]
I'm not fussy about inplace or create a new list, but right now this smells to me. I can't see a way to store a temporary 'last' value and use map/lambda, and nothing else is coming to mind.
Solution 1:[1]
a = [None,1,2,3,None,4,None,None]
start = next(ele for ele in a if ele is not None)
for ind, ele in enumerate(a):
if ele is None:
a[ind] = start
else:
start = ele
print(a)
[1, 1, 2, 3, 3, 4, 4, 4]
You also only need to set start to a value if the first element is None:
if a[0] is None:
start = next(ele for ele in a if ele is not None)
for ind, ele in enumerate(a):
if ele is None:
a[ind] = start
else:
start = ele
print(a)
Solution 2:[2]
IIUC, you could use itertools.accumulate
to generate a forward fill:
>>> from itertools import accumulate
>>> a = [None,1,2,3,None,4,None,None]
>>> list(accumulate(a, lambda x,y: y if y is not None else x))
[None, 1, 2, 3, 3, 4, 4, 4]
Solution 3:[3]
Here's some code that will do what you want in place, if you don't want it in place then just pass it list(my_list)
instead of my_list
.
def replaceNoneWithLeftmost(val):
for i in range(len(val)):
if val[i] is None:
for j in range(i-1, -1, -1):
if val[j] is not None:
val[i] = val[j]
break
for i in range(len(val)):
if val[i] is None:
for j in range(i+1, len(val)):
if val[j] is not None:
val[i] = val[j]
break
return val
Also, if using python2, use xrange
instead of range
.
Solution 4:[4]
a = [None,1,2,3,None,4,None,None]
first = True
for i in range(len(a)):
if first:
if a[i] != None:
a[:i] = [a[i] for _ in range(i)]
first = False
if a[i] == None:
a[i] = a[i-1]
print a
OUT:
[1, 1, 2, 3, 3, 4, 4, 4]
Solution 5:[5]
It can also be done in the following approach one function to find the first non None element in the given list and the second will propagate the previous non None elements forward.
pure python...
l = [None , None , None ,None ,10, 1, 2, 3, None, 4, None, None]
def get_first_non_empty(lst):
empty = True
for i in lst:
if i is not None:
while empty:
empty = False
break
return i
def get_last_non_empty(lst):
new_lst = []
prev = None
for itm in lst:
if itm is not None:
prev = itm
new_lst.append(itm)
else:
if len(new_lst)==0 or prev is None:
new_lst.append(get_first_non_empty(lst))
else:
new_lst.append(prev)
return new_lst
print (get_last_non_empty(l))
# input ->[None , None , None ,None ,10, 1, 2, 3, None, 4, None, None]
# output ->[10, 10, 10, 10, 10, 1, 2, 3, 3, 4, 4, 4]
Note that there night be several None values at the beginning of the list. this two functions handle this scenario
Solution 6:[6]
A very simple and basic approach to the solution
lis = []
var = a[0]
for i in a:
if i:
lis.append(i)
var = i
elif i != 0:
lis.append(var)
else:
lis.append(i)
var = a[0]
was added just to remove Out of range
error. The above code outputs [None, 1, 2, 3, 3, 4, 4, 4]
. It iterates over the list a; if the value is non-None, it adds it to the list, otherwise, it adds the last non-None value, stored in var variable. The last else is added for cases where the first element is None, then it adds None as it is to the list.
Solution 7:[7]
Here is a solution that keeps the time complexity (Big O) at O(n):
def replaceNoneWithLeftmost(val):
i = 0
r = [None] * len(val)
for i in range(len(val)):
if val[i] is None:
r[i] = r[i-1]
else:
r[i] = val[i]
i += 1
return r
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | DSM |
Solution 3 | CrazyCasta |
Solution 4 | Geeocode |
Solution 5 | hkravitz |
Solution 6 | Swati Srivastava |
Solution 7 | Tolu |