Here is what I exactly want:
A = [1,2,3]
for a in A:
if a > 1:
del a
print(A)
I want list a to be [1], however it will still be [1,2,3].
So my real goal is to delete current element I get now.
I know there is one usage of 'del' liking this:
A = [1,2,3]
for i, a in enumerate(A):
if a > 1:
del A[i]
print(A)
and list a will be [1,3] but not [1].
Because when I use
del a[1] #2
the orginally 3 in list A will be needed to use index 1 to get it from list A rather than using 2. So I can not delete it successfully.
How can I delete the current element in the list?
It is almost never a good idea to change a collection you are iterative over. In many cases, a better way would be to select only these elements that you want in your new list.
A pythonesque way to do this is using list comprehension:
a = [1,2,3]
b = [x for x in a if x <= 1]
print (b)
the '[ .. for ... if ..]' is a very nice syntax for all kinds of list operations like map and filter.
Notice that I complemented your filter predicate, because I select what has to stay, in stead of what has to be deleted.
Hope this helps!!
B = filter(lambda x: x <= 1, A)
Then B is all elements that less than or equal 1 of A.
In general, you must be very careful while modifying the data structure if you are currently iterating over its elements. Python will give you an error if you try to delete elements from a list while iterating over the elements of the list. If you really want to modify the list, then use a different iteration strategy, e.g.
A = [1, 2, 3, 1, 2, 3]
n = len(A)
i = 0
while i < n:
if A[i] > 1:
del A[i]
n -= 1
else:
i += 1
print(A)
This is one way to get the result you want.
>>> [i for i in [1,2,3] if i<=1]
[1]
>>> list(filter(lambda x:x<=1, [1,2,3]))
[1]
>>>
all list elements that are >1 are filtered out
In your Code when you are trying to delete the value
del a
You actually were trying to delete a temporary variable a whose value is same as the value of A[X] . So the value in A still remains unaffected. If you want to delete the value from A, you can make use of
del A[X]
a.remove(value)
these methods will delete/remove the value from the List .
Related
I've had a look through the forums and can't find anything to do with multiplying all elements in an array recursively.
I've created the following code that almost does what I want. The goal is to use no loops and only recursion.
Here's the code:
def multAll(k,A):
multAllAux(k,A)
return A[:]
def multAllAux(k,A):
B = [0]
if A == []:
return 0
else:
B[0] = (A[0] * k)
B.append(multAllAux(k,A[1:]))
return B
print(multAllAux(10, [5,12,31,7,25] ))
The current output is:
[50, [120, [310, [70, [250, 0]]]]]
However, it should be:
[50,120,310,70,250]
I know I am close, but I am at a complete loss at this point. The restrictions of no loops and solely recursion has left me boggled!
Your multAllAux function returns a list. If you append a list to another list, you get this nested list kind of structure that you are getting right now.
If you instead use the "extend" function; it will work as expected.
>>> a = [1, 2, 3]
>>> a.extend([4, 5])
>>> a
[1, 2, 3, 4, 5]
extend takes the elements from a second list and adds them to the first list, instead of adding the second list itself which is what append does!
Your function also returns a zero at the end of the list, which you don't need. You can try this:
def mult(k, A: list) -> list:
return [k * A[0]] + mult(k, A[1:]) if A else []
The problem is here:
B.append(multAllAux(k,A[1:])))
What .append(..) does is it takes the argument, considers it as a single element and adds that element to the end of the list. What you want is to concatenate to the list (ie the item being added should be seen as a list of elements rather than one single element).
You can say something like: B = B + multAllAux(..) or just use +=
B += multAllAux(...)
BTW, if you wanted to multiply a single number to a list, there is a very similar construct: map(..). Note that this behaves slightly differently depending on whether you're using Py2 or Py3.
print(map(lambda x: x * 10, [5,12,31,7,25]))
I am trying to perform in-place modification of a list of list on the level of the primary list. However, when I try to modify the iterating variable (row in the example below), it appears to create a new pointer to it rather than modifying it.
Smallest example of my problem.
c = [1,2,3]
for x in c:
x = x + 3
print(c) #returns [1,2,3], expected [4,5,6]
The above example is a trivial example of my problem. Is there a way to modify x elementwise, in-place and have the changes appear in C?
Less trivial example of my problem. I am switching all 0's to 1's and vice-versa.
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row = list(map(lambda val: 1 - val, row))
print(A)
Expected
A = [[0,0,1],
[0,1,0],
[1,1,1]]
Returned
A = [[1,1,0],
[1,0,1],
[0,0,0]]
update:
Great answers so far. I am interested how the iterating variable (row in the second example) is linked to the iterable variable (A in the second example).
If I do the following, which reverses each sublist of A, it works perfectly.
Why does the following example, where I modify the iterating variable works but the above examples do not?
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row.reverse()
print(A)
#returns, as expected
A = [[0, 1, 1],
[1, 0, 1],
[0, 0, 0]]
I found this in the docs: https://docs.python.org/3/tutorial/controlflow.html#for
Python’s for statement iterates over the items of any sequence (a list
or a string), in the order that they appear in the sequence.
If you need to modify the sequence you are iterating over while inside
the loop (for example to duplicate selected items), it is recommended
that you first make a copy. Iterating over a sequence does not
implicitly make a copy.
I was wrong in my first response, when iterating through a list it returns the actual items in that list. However, it seems they cannot be edited directly while they are being iterated through. This is why iterating through the integers the length of the list works.
As for why the .reverse() function works, I think it's because it is affecting a list instead of a value. I tried to use similar built in functions on nonlist datatypes like .replace() on strings and it had no effect.
All of the other list functions I tried worked: .append(), .remove(), and .reverse() as you showed. I'm not sure why this is, but I hope it clears up what you can do in for loops a bit more.
Answer to old question below:
The way you are using the for loops doesn't affect the actual list, just the temporary variable that is iterating through the list. There are a few ways you can fix this. Instead of iterating through each element you can can count up to the length of the list and modify the list directly.
c = [1,2,3]
for n in range(len(c)):
c[n] += 3
print(c)
You can also use the enumerate() function to iterate through both a counter and list items.
c = [1,2,3]
for n, x in enumerate(c):
c[n] = x + 3
print(c)
In this case, n is a counter and x is the item in the list.
Finally, you can use list comprehension to generate a new list with desired differences in one line.
c = [1, 2, 3]
d = [x + 3 for x in c]
print(d)
The usual way to poke values into an existing list in Python is to use enumerate which lets you iterate over both the indices and the values at once -- then use the indices to manipulate the list:
c = [1,2,3]
for index, value in enumerate(c):
c[index] = value + 3
For your second example you'd do almost the same:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
for index, val in row:
row[index] = 0 if val > 0 else 1
In the second example the list objects in A become the loop variable row -- and since you're only mutating them (not assigning to them) you don't need enumerate and the index
If you want to keep it consice without creating an additional variable, you could also do:
c = [1,2,3]
print(id(c))
c[:] = [i+3 for i in c]
print(c, id(c))
Output:
2881750110600
[4, 5, 6] 2881750110600
Using list comprehension here also will work:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
A = [[0 if x > 0 else 1 for x in row] for row in A]
print(A)
Output:
[[0, 0, 1],
[0, 1, 0],
[1, 1, 1]]
I am using python 2.7. I am trying to simulate popping of element from a list using the pop function. But I'm getting an inconsistent result.
Code using a variable list
list_pop = [1,2,3]
for x in list_pop:
print list_pop
y = list_pop.pop(0)
print y
Result:
[1, 2, 3]
1
[2, 3]
2
Code without using variable to hold the list
list_pop = [1,2,3]
for x in [1,2,3]:
print list_pop
y = list_pop.pop(0)
print y
[1, 2, 3]
1
[2, 3]
2
[3]
3
I have tried popping index 0 from a list with one element it's working. I'm wondering why my first code halts printing after number 2 but when I print the list_pop it gives [3] the first code should still iterate through it and print 3. I'm expecting same results for both code. What am I missing here?
In the first case, you are iterating over the list while modifying it, which will almost certainly give you unexpected results.
In the second case, you are not iterating over the same list you are changing, so the result is a fixed number of iterations.
If you wanted to do the first one and examine all elements, a while loop may work better.
while not len(list_pop) == 0:
print list_pop
y = list_pop.pop(0)
print y
Another alternative is to make a copy of the list and iterate over the copy:
for x in list_pop[:]:
print list_pop
y = list_pop.pop(0)
print y
A good general rule to follow is that you shouldn't modify a list that you are iterating over. This is exactly the behavior you should get: the list you are iterating over gets smaller and smaller, so the loop will have fewer and fewer items to iterate over. It doesn't iterate over the original list, it iterates over the current list.
In the second example you iterate over a list that never changes, so you get exactly three iterations.
You might want to take a look at this question: Remove items from a list while iterating
You should not modify your list while looping through it. That's why the results are inconsistent. Instead you can use a while loop and check is list empty:
while list_pop:
print list_pop
y = list_pop.pop(0)
print y
>>> n = [1, 2, 3]
>>> for item in n:
... item *= 2
...
>>> print n
[1, 2, 3]
I expect the result of the above code to be [2, 4, 6], While obviously it's not.
Then I tried for i in range(n) as follows
>>> n = [1, 2, 3]
>>> for i in range(len(n)):
... n[i] *= 2
...
>>>
>>> n
[2, 4, 6]
This seems OK.
And my question is that, what's the essential difference between these two for loop method? What cause the unexpected result above?
If it helps, the first loop is equivalent to:
for i in range(len(n)):
item = n[i]
item *= 2
In other words, it first binds item to the i-th element of the list, and then rebinds it to a new object whose value is double that of the i-th element. It does not change any of the list's elements.
A good way to implement this loop is using a list comprehension:
n = [item * 2 for item in n]
You can't modify the object that represents the current iteration.
Well, actually, you can, but it won't change the object that is held in the list.
what's the essential difference between these two for loop method?
You iterate over objects in the list in the first example (and try to modify that said object directly - it doesn't change the list's element itself).
And you iterate over the list of integers in the second example (and actually modify the given list's element, so you modify the list content).
item is simply a local name. It is originally assigned by the for loop to point to the current element, but if you reassign it to point to something else, that has no effect on the original.
But if you use an index to reference an element in the original list, you can mutate that list to contain different values.
There's no assignment in for item in lst. You're operating on the object itself, which is immutable, so it just creates a new object for you when you do the *= call, assigns it to item, then throws it away on the next iteration of the loop.
When you do for i in range(len(lst)) you're assigning the new object to the ith element of lst.
I just started Python classes and I'm really in need of some help. Please keep in mind that I'm new if you're answering this.
I have to make a program that takes the average of all the elements in a certain list "l". That is a pretty easy function by itself; the problem is that the teacher wants us to remove any empty string present in the list before doing the average.
So when I receive the list [1,2,3,'',4] I want the function to ignore the '' for the average, and just take the average of the other 4/len(l). Can anyone help me with this?
Maybe a cycle that keeps comparing a certain position from the list with the '' and removes those from the list? I've tried that but it's not working.
You can use a list comprehension to remove all elements that are '':
mylist = [1, 2, 3, '', 4]
mylist = [i for i in mylist if i != '']
Then you can calculate the average by taking the sum and dividing it by the number of elements in the list:
avg = sum(mylist)/len(mylist)
Floating Point Average (Assuming python 2)
Depending on your application you may want your average to be a float and not an int. If that is the case, cast one of these values to a float first:
avg = float(sum(mylist))/len(mylist)
Alternatively you can use python 3's division:
from __future__ import division
avg = sum(mylist)/len(mylist)
You can use filter():
filter() returns a list in Python 2 if we pass it a list and an iterator in Python 3. As suggested by #PhilH you can use itertools.ifilter() in Python 2 to get an iterator.
To get a list as output in Python 3 use list(filter(lambda x:x != '', lis))
In [29]: lis = [1, 2, 3, '', 4, 0]
In [30]: filter(lambda x:x != '', lis)
Out[30]: [1, 2, 3, 4, 0]
Note to filter any falsy value you can simply use filter(None, ...):
>>> lis = [1, 2, 3, '', 4, 0]
>>> filter(None, lis)
[1, 2, 3, 4]
The other answers show you how to create a new list with the desired element removed (which is the usual way to do this in python). However, there are occasions where you want to operate on a list in place -- Here's a way to do it operating on the list in place:
while True:
try:
mylist.remove('')
except ValueError:
break
Although I suppose it could be argued that you could do this with slice assignment and a list comprehension:
mylist[:] = [i for i in mylist if i != '']
And, as some have raised issues about memory usage and the wonders of generators:
mylist[:] = (i for i in mylist if i != '')
works too.
itertools.ifilterfalse(lambda x: x=='', myList)
This uses iterators, so it doesn't create copies of the list and should be more efficient both in time and memory, making it robust for long lists.
JonClements points out that this means keeping track of the length separately, so to show that process:
def ave(anyOldIterator):
elementCount = 0
runningTotal = 0
for element in anyOldIterator:
runningTotal += element
elementCount += 1
return runningTotal/elementCount
Or even better
def ave(anyOldIterator):
idx = None
runningTotal = 0
for idx,element in enumerate(anyOldIterator):
runningTotal += element
return runningTotal/(idx+1)
Reduce:
def ave(anyOldIterator):
pieces = reduce(lambda x,y: (y[0],x[1]+y[1]), enumerate(anyOldIterator))
return pieces[1]/(pieces[0]+1)
Timeit on the average of range(0,1000) run 10000 times gives the list comprehension a time of 0.9s and the reduce version 0.16s. So it's already 5x faster before we add in filtering.
You can use:
alist = ['',1,2]
new_alist = filter(None, alist)
new_alist_2 = filter(bool, alist)
Result:
new_alist = [1,2]
new_alist_2 = [1,2]
mylist = [1, 2, 3, '', 4]
newlist = []
for i in mylist:
try:
newlist.append(int(i))
except ValueError:
pass
avg = sum(newlist)/len(newlist)
'' is equivalent to False. If we filter the 0 case out (because 0 is equivalent to False), we can use list comprehension :
[x for x in a if x or x == 0]
Or if we strictly want to filter out empty strings :
[x for x in a if x != '']
This may not be the fastest way.
Edit, added some bench results comparing with the other solutions (not for the sake of comparing myself to others, but I was curious too of what method was the fastest)
ragsagar>
6.81217217445
pistache>
1.0873541832
cerealy>
1.07090902328
Matt>
1.40736508369
Ashwini Chaudhary>
2.04662489891
Phil H (just the generator) >
0.935978889465
Phil H with list() >
3.58926296234
I made the script quickly, using timeit(), I used [0,1,2,0,3,4,'',5,8,0,'',4] as the list. I ran multiple tests, results did not vary.
NOTE: I'm not trying to put my solution on top using speed as a criteria. I know OP didn't specifically ask for speed, but I was curious and maybe some other are.