1

I have the following list a with None values for which I want to make a "fill down".

a = [
        ['A','B','C','D'],
        [None,None,2,None],
        [None,1,None,None],
        [None,None,8,None],
        ['W','R',5,'Q'],
        ['H','S','X','V'],
        [None,None,None,7]
    ]

The expected output would be like this:

b = [
        ['A','B','C','D'],
        ['A','B',2,'D'],
        ['A',1,'C','D'],
        ['A','B',8,'D'],
        ['W','R',5,'Q'],
        ['H','S','X','V'],
        ['H','S','X',7]
    ]

I was able to make the next code and seems to work but I was wondering if there is a built-in method or more direct way to do it. I know that there is something like that using pandas but needs to convert to dataframe, and I want to continue working with list, if possible only update a list, and if not possible to modify a then get output in b list. Thanks

b = []
for z in a:
    if None in z:
        b.append([temp[i] if value == None else value for i, value in enumerate(z) ])
    else:
        b.append(z)
        temp = z
Rasec Malkic
  • 373
  • 1
  • 8
  • 1
    your solution seems absolutely appropriate ... – Joran Beasley Oct 22 '22 at 03:34
  • Surely `['A',1,'C','D']` in the expected output is wrong? Shouldn't it be `['A',1, 2,'D']`, since the previous row has a `2` in that column? Similarly I expect `['A','B',8,'D']` should be `['A',1,8,'D']`. – Karl Knechtel Oct 22 '22 at 05:27
  • @KarlKnechtel Actually, I want to carried down the rows that have letters until the next row-1 that have letters – Rasec Malkic Oct 22 '22 at 06:17

2 Answers2

2

You could use a list comprehension for this, but I'm not sure it adds a lot to your solution already.

b = [a[0]]
for a_row in a[1:]:
    b.append([i if i else j for i,j in zip(a_row, b[-1])])

I'm not sure if it's by design, but in your example a number is never carried down to the next row. If you wanted to ensure that only letters are carried down, this could be added by keeping track of the letters last seen in each position. Assuming that the first row of a is always letters then;

last_seen_letters = a[0]
b = []
for a_row in a:
    b.append(b_row := [i if i else j for i,j in zip(a_row, last_seen_letters)])
    last_seen_letters = [i if isinstance(i, str) else j for i,j in zip(b_row, last_seen_letters)]
bn_ln
  • 1,648
  • 1
  • 6
  • 13
  • 1
    Excellent.Since I'm new to python, is good to see new options and how you use zip() in this case. I haven't had in my radar hehe. Normally letters/words or numbers can be carried down in actual data, but your second solution is an option to learn for me. Thank you – Rasec Malkic Oct 22 '22 at 06:16
1

First, consider the process of "filling down" into a single row. We have two rows as input: the row above and the row below; we want to consider elements from the two lists pairwise. For each pair, our output is determined by simple logic - use the first value if the second value is None, and the second value otherwise:

def fill_down_new_cell(above, current):
    return above if current is None else current

which we then apply to each pair in the pairwise iteration:

def fill_down_new_row(above, current):
    return [fill_down_new_cell(a, c) for a, c in zip(above, current)]

Next we need to consider overlapping pairs of rows from our original list. Each time, we replace the contents of the "current" row with the fill_down_row result, by slice-assigning them to the entire list. In this way, we can elegantly update the row list in place, which allows changes to propagate to the next iteration. So:

def fill_down_inplace(rows):
    for above, current in zip(rows, rows[1:]):
        current[:] = fill_down_new_row(above, current)

Let's test it:

>>> a = [
...         ['A','B','C','D'],
...         [None,None,2,None],
...         [None,1,None,None],
...         [None,None,8,None],
...         ['W','R',5,'Q'],
...         ['H','S','X','V'],
...         [None,None,None,7]
...     ]
>>> fill_down_inplace(a)
>>> import pprint
>>> pprint.pprint(a)
[['A', 'B', 'C', 'D'],
 ['A', 'B', 2, 'D'],
 ['A', 1, 2, 'D'],
 ['A', 1, 8, 'D'],
 ['W', 'R', 5, 'Q'],
 ['H', 'S', 'X', 'V'],
 ['H', 'S', 'X', 7]]
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153