In How to modify list entries during for loop?, the general recommendation is that it can be unsafe so don't do it unless you know it's safe. In comments under the first answer @martineau says:
It [the loop variable] doesn't make a copies. It just assigns the loop variable name to successive elements or value of the thing being iterated-upon.
That is the behavior I expect and want, but I'm not getting it. I want to remove trailing None
s from each value in the list. My loop variable is modified correctly but the list elements remain unchanged.
How can I get the loop variable fv
to be a pointer to the rows of list foo
, not a copy of the row?
Extra credit: my code is sucky non-pythonic, so a solution using comprehensions or slices instead of for loops would be preferred.
foo = [
['a', 'b', None, None],
['c', None, 'e', None],
['f', None, None, None]
]
desired = [
['a', 'b'],
['c', None, 'e'],
['f']
]
for fv in foo:
for v in range(len(fv) -1, 0, -1):
if fv[v] == None:
fv = fv[:v]
print(f' {v:2} {fv}')
else:
break
print(foo)
The output is:
3 ['a', 'b', None]
2 ['a', 'b']
3 ['c', None, 'e']
3 ['f', None, None]
2 ['f', None]
1 ['f']
[['a', 'b', None, None], ['c', None, 'e', None], ['f', None, None, None]]
In How to modify list entries during for loop?, the general recommendation is that it can be unsafe so don't do it unless you know it's safe. In comments under the first answer @martineau says:
It [the loop variable] doesn't make a copies. It just assigns the loop variable name to successive elements or value of the thing being iterated-upon.
That is the behavior I expect and want, but I'm not getting it. I want to remove trailing None
s from each value in the list. My loop variable is modified correctly but the list elements remain unchanged.
How can I get the loop variable fv
to be a pointer to the rows of list foo
, not a copy of the row?
Extra credit: my code is sucky non-pythonic, so a solution using comprehensions or slices instead of for loops would be preferred.
foo = [
['a', 'b', None, None],
['c', None, 'e', None],
['f', None, None, None]
]
desired = [
['a', 'b'],
['c', None, 'e'],
['f']
]
for fv in foo:
for v in range(len(fv) -1, 0, -1):
if fv[v] == None:
fv = fv[:v]
print(f' {v:2} {fv}')
else:
break
print(foo)
The output is:
3 ['a', 'b', None]
2 ['a', 'b']
3 ['c', None, 'e']
3 ['f', None, None]
2 ['f', None]
1 ['f']
[['a', 'b', None, None], ['c', None, 'e', None], ['f', None, None, None]]
Share
Improve this question
edited 14 hours ago
Selcuk
59.2k12 gold badges110 silver badges115 bronze badges
asked 14 hours ago
DaveDave
3,9562 gold badges34 silver badges45 bronze badges
2 Answers
Reset to default 2The line fv = fv[:v]
will create a new variable named fv
instead of mutating fv
. You should mutate the existing list.
One solution could be using while
to strip unwanted values until none left. The .pop()
method will mutate row
instead of returning a new list:
for row in foo:
while row and row[-1] is None:
row.pop()
assert foo == desired
You can go from the end of the list and remove a range of elements starting from the first non-zero element. If you have a lot of None elements (average more then 50), this approach faster, then @Selcuk answer
for row in foo:
last_null_index = 0
for index, element in enumerate((reversed(row))):
if element is not None:
last_null_index = index
break
# for case when all of elements is not None
if last_null_index == 0:
continue
row[-last_null_index:] = []
assert foo == desired