Reputation: 28282
I know that it is not safe to modify the list during an iterative looping. However, suppose I have a list of strings, and I want to strip the strings themselves. Does replacement of mutable values count as modification?
See Why doesn't modifying the iteration variable affect subsequent iterations? for a related problem: assigning to the iteration variable does not modify the underlying sequence, and also does not impact future iteration.
Upvotes: 266
Views: 459034
Reputation: 3
In this example, the objective is to, using a Python 'for' loop, REMOVE from an array of integers any element that is perfectly divisible by 7:
def modify_array(numbers):
for number in numbers:
print ((number, numbers))
if ((number % 7) == 0):
numbers.remove(numbers[numbers.index(number)])
modify_array(numbers)
return (numbers)
numbers = [1, 2, 3, 4, 5, 6, 7, 14, 9, 77]
numbers = modify_array(numbers)
print(numbers)
The above 'iterative recursion' approach is one that works, as doing an ordinary for loop would miss the number 14 in the array.
Upvotes: 0
Reputation: 5982
Something I just discovered - when looping over a list of mutable types (such as dictionaries) you can just use a normal for loop like this:
l = [{"n": 1}, {"n": 2}]
for d in l:
d["n"] += 1
print(l)
# prints [{"n": 2}, {"n": 1}]
Upvotes: 0
Reputation: 91
The answer given by Ignacio Vazquez-Abrams is really good. It can be further illustrated by this example. Imagine that:
Let's say you have:
v = np.array([1,2,3,4])
b = np.array([3,4,6])
for i in [v, b]:
i = i[::-1] # This command does not reverse the string.
print([v,b])
You will get:
[array([1, 2, 3, 4]), array([3, 4, 6])]
On the other hand, if you do:
v = np.array([1,2,3,4])
b = np.array([3,4,6])
for i in [v, b]:
i[:] = i[::-1] # This command reverses the string.
print([v,b])
The result is:
[array([4, 3, 2, 1]), array([6, 4, 3])]
Upvotes: 6
Reputation: 123541
Since the loop below only modifies elements already seen, it would be considered acceptable:
a = ['a',' b', 'c ', ' d ']
for i, s in enumerate(a):
a[i] = s.strip()
print(a) # -> ['a', 'b', 'c', 'd']
Which is different from:
a[:] = [s.strip() for s in a]
in that it doesn't require the creation of a temporary list and an assignment of it to replace the original, although it does require more indexing operations.
Caution: Although you can modify entries this way, you can't change the number of items in the list
without risking the chance of encountering problems.
Here's an example of what I mean—deleting an entry messes-up the indexing from that point on:
b = ['a', ' b', 'c ', ' d ']
for i, s in enumerate(b):
if s.strip() != b[i]: # leading or trailing whitespace?
del b[i]
print(b) # -> ['a', 'c '] # WRONG!
(The result is wrong because it didn't delete all the items it should have.)
Update
Since this is a fairly popular answer, here's how to effectively delete entries "in-place" (even though that's not exactly the question):
b = ['a',' b', 'c ', ' d ']
b[:] = [entry for entry in b if entry.strip() == entry]
print(b) # -> ['a'] # CORRECT
See How to remove items from a list while iterating?.
Upvotes: 256
Reputation: 399
In short, to do modification on the list while iterating the same list.
list[:] = ["Modify the list" for each_element in list "Condition Check"]
example:
list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
Upvotes: 1
Reputation: 799450
It's considered poor form. Use a list comprehension instead, with slice assignment if you need to retain existing references to the list.
a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)
Upvotes: 179
Reputation: 197
You can do something like this:
a = [1,2,3,4,5]
b = [i**2 for i in a]
It's called a list comprehension, to make it easier for you to loop inside a list.
Upvotes: 4
Reputation: 13991
Modifying each element while iterating a list is fine, as long as you do not change add/remove elements to list.
You can use list comprehension:
l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]
or just do the C-style
for loop:
for index, item in enumerate(l):
l[index] = item.strip()
Upvotes: 18
Reputation: 431
One more for loop variant, looks cleaner to me than one with enumerate():
for idx in range(len(list)):
list[idx]=... # set a new value
# some other code which doesn't let you use a list comprehension
Upvotes: 27
Reputation: 191
It is not clear from your question what the criteria for deciding what strings to remove is, but if you have or can make a list of the strings that you want to remove , you could do the following:
my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
for i in range(my_strings.count(undesirable_string)):
my_strings.remove(undesirable_string)
which changes my_strings to ['a', 'c', 'e']
Upvotes: 1
Reputation: 22179
No you wouldn't alter the "content" of the list, if you could mutate strings that way. But in Python they are not mutable. Any string operation returns a new string.
If you had a list of objects you knew were mutable, you could do this as long as you don't change the actual contents of the list.
Thus you will need to do a map of some sort. If you use a generator expression it [the operation] will be done as you iterate and you will save memory.
Upvotes: 4