7

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.

Alex Riley
  • 169,130
  • 45
  • 262
  • 238
tlrrd
  • 322
  • 3
  • 8

7 Answers7

6

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]
DSM
  • 342,061
  • 65
  • 592
  • 494
3
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)
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
0

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.

CrazyCasta
  • 26,917
  • 4
  • 45
  • 72
0
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]
Geeocode
  • 5,705
  • 3
  • 20
  • 34
0

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

hkravitz
  • 1,345
  • 1
  • 10
  • 20
0

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.

Swati Srivastava
  • 1,102
  • 1
  • 12
  • 18
0

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
Tolu
  • 175
  • 4
  • 10