Python: Editing list while iterating over it - python

There are some related questions on Stack but I wanted to be as clear as possible. I am using Python 3
If I have a list, N, and I use the following code:
N = [1,2,3,4,6,7,8]
for x in N:
N[x] = N[x] * -1
return N
I am getting a Index out of range error. I understand you shouldn't be adding and deleting elements while iterating over a list, but I wanted a clear definition of why the above example won't work.To me, it seems like there shouldn't be a problem. In the first iteration, x should evaluate to 1. So if I want to edit N[1], I don't see why I wouldn't be able to.
As a side note, I know enumerate() is the proper way to do it.

Use enumerate
Ex:
N = [1,2,3,4,6,7,8]
for x, value in enumerate(N):
N[x] = value * -1
print(N)
or a list comprehension.
Ex:
N = [x * -1 for x in N]

In for x in N:, x takes on each value in N, then you use it like an index. Lists in Python are 0-indexed, so you get an Index out of range error when you reach the end of the list and try to access N[8], which doesn't exist. You can use for x in range(len(N)): instead.

The problem here is actually a little different: you're treating members of the list as if they were indices of the list. Since N[8] doesn't exist, that's where your error is coming from. I think what you meant to do was:
N = [1,2,3,4,6,7,8]
for x in range(len(N)):
N[x] = N[x] * -1
return N

In the first iteration, x=1 which means N[1] equals 2 and so your new N[1] becomes 2 * -1 = -2. Now when x is 8 since you are using for x in N, your code tries to access N[8] but since the length of N is 8, the indexing starts from 0 and goes up to 7. Hence there is no index 8 and hence you get an error Index out of range error

Use good ol' lambda's or a specific callable in list comprehension:
lambda:
N = [1,2,3,4,6,7,8]
M = [(lambda x:-x)(element) for element in N]
print(M)
callable/function:
def negate(x):
return -x
N = [1,2,3,4,6,7,8]
M = [negate(element) for element in N]
print(M)

In your code here -
N = [1,2,3,4,6,7,8]
for x in N:
# x will be 1, 2, 3, 4
And the way you are accessing is -
N[x]
But indices run from 0 to n-1, where n-1 is the last element. Also, in your list, you are missing a 5 so indices will change badly. If you had
N = [1,2,3,4,5,6,7,8]
your code would have worked fine. But since that's not the case, you'd have to use range like -
for i in range(len(N)):
N[i] = N[i] * -1

You are enumerating in a wrong way
N = [1,2,3,4,6,7,8]
for i, x in enumerate(N):
N[i] = N[i] * -1
return N

Related

Euler method for two dimension system

I am absolutely a beginner in programming. and trying to write an algorithm of the Euler method for interaction two systems. When I run the code I got this Error message. I will appreciate it if you help me. Thank you.
IndexError: list assignment index out of range
def NmDef(U1, V1, U2, V2):
Nm = math.sqrt((U1-V1)**2+(U2-V2)**2) #sqrt
return Nm
NmDef_value = []
for i in range(n):
i +=1
L = NmDef(U[i], V[i], U[i-1], V[i-1])
print(i, L)
NmDef_value.append(L)
I can see that you have many errors in the for loops.
First in this for loop you have:
NmDef_value = []
for i in range(n):
i +=1
L = NmDef(U[i], V[i], U[i-1], V[i-1])
print(i, L)
NmDef_value.append(L)
In this code the foor loop is incrementing the i value by 1 on each step. In you case it is incrementing it by 1 and you are also incrementing it by 1 so on each iteration you are incrementing i by 2. When you will arrive at the last element of the list you will have i = n and you can only take the element of a list with the value n-1 or else you will get index out of range. You can fix it like that:
NmDef_value = []
for i in range(1,n):
L = NmDef(U[i], V[i], U[i-1], V[i-1])
print(i, L)
NmDef_value.append(L)
For the second for loop:
for i in range (n):
U[i]=U[i-1]+dt*(Fa[i]+Fr[i])
V[i]=V[i-1]-dt*(Fa[i]+Fr[i])
for i in range (n):
print(U[i], V[i])
In this one for i = 0 you are trying to access U[i-1] so it is U[-1] and it will be out of range too. You can add a can start the for loop with 1 like that: for i in range(1,n)

Use list comprehensions to make a list of count of elements smaller than the element in an array

I was solving this leetcode problem - https://leetcode.com/problems/how-many-numbers-are-smaller-than-the-current-number/
I solved it easily by using nested for loops but list comprehensions have always intrigued me. Ive spent a lot of time to make that one liner work but I always get some syntax error.
here's the solution:
count = 0
ans = []
for i in nums:
for j in nums:
if i > j:
count = count + 1
ans.append(count)
count = 0
return ans
these were the ones so far I think shouldve worked:
return [count = count + 1 for i in nums for j in nums if i > j]
return [count for i in nums for j in nums if i > j count = count + 1]
return [count:= count + 1 for i in nums for j in nums if i > j]
Ill also be happy if there's some resource or similar to put it together, Ive been searching the python docs but didnt find something that'll help me
I will transform the code step by step in order to show the thought process.
First: we don't care what the value of count is afterward, but we need it to be 0 at the start of each inner loop. So it is simpler logically to set it there, rather than outside and then also at the end of the inner loop:
ans = []
for i in nums:
count = 0
for j in nums:
if i > j:
count = count + 1
ans.append(count)
return ans
Next, we focus on the contents of the loop:
count = 0
for j in nums:
if i > j:
count = count + 1
ans.append(count)
A list comprehension is not good at math; it is good at producing a sequence of values from a source sequence. The transformation we need to do here is to put the actual elements into our "counter" variable1, and then figure out how many there are (in order to append to ans). Thus:
smaller = []
for j in nums:
if i > j:
smaller.append(j)
ans.append(len(smaller))
Now that the creation of smaller has the right form, we can replace it with a list comprehension, in a mechanical, rule-based way. It becomes:
smaller = [j for j in nums if i > j]
# ^ ^^^^^^^^^^^^^ ^^^^^^^^
# | \- the rest of the parts are in the same order
# \- this moves from last to first
# and then we use it the same as before
ans.append(len(smaller))
We notice that we can just fold that into one line; and because we are passing a single comprehension argument to len we can drop the brackets2:
ans.append(len(j for j in nums if i > j))
Good. Now, let's put that back in the original context:
ans = []
for i in nums:
ans.append(len(j for j in nums if i > j))
return ans
We notice that the same technique applies: we have the desired form already. So we repeat the procedure:
ans = [len(j for j in nums if i > j) for i in nums]
return ans
And of course:
return [len(j for j in nums if i > j) for i in nums]
Another popular trick is to put a 1 in the output for each original element, and then sum them. It's about the same either way; last I checked the performance is about the same and I don't think either is clearer than the other.
Technically, this produces a generator expression instead. Normally, these would be surrounded with () instead of [], but a special syntax rule lets you drop the extra pair of () when calling a function with a single argument that is a generator expression. This is especially convenient for the built-in functions len and sum - as well as for any, all, max, min and (if you don't need a custom sort order) sorted.
Hmm, three people write sum solutions but every single one does sum(1 for ...). I prefer this:
[sum(j < i for j in nums) for i in nums]
Instead of trying to advance an external counter, try adding ones to your list and then sum it:
for example:
nums = [1,2,3,4,5]
target = 3
print(sum(1 for n in nums if n < target))
Using counter inside the list comprehension creates the challenge of resetting it's value, each iteration of the first loop.
This can be avoided by filtering, and summing, in the second loop:
You use the first loop to iterate over the values of nums array.
return [SECOND_LOOP for i in nums]
You use the second loop, iterating over all elements of nums array. You filter in the elements that are smaller than i, the current element in the first loop, with if i < j, and evaluating 1 for each of them. Finally, you sum all the 1s generated:
sum(1 for j in nums if i > j)
You get the number of values that meet the requirements, by the list comprehension of the first loop:
return [sum(1 for j in nums if i > j) for i in nums]
This solution has been checked & validated in LeetCode.
You need a slightly different approach for the inner loop than a list comprehension. Instead of repeatedly appending a value to a list you need to repeatedly add a value to a variable.
This can be done in a functional way by using sum and a generator expression:
count = 0
# ...
for j in nums:
if i > j:
count = count + 1
can be replaced by
count = sum(1 for j in nums if i > j)
So that we now have this:
ans = []
for i in nums:
count = sum(1 for j in nums if i > j)
ans.append(count)
return ans
This pattern can in fact be replaced by a list comprehension:
return [sum(1 for j in nums if i > j) for i in nums]
Alternative Solution
We can also use the Counter from collections:
class Solution:
def smallerNumbersThanCurrent(self, nums):
count_map = collections.Counter(nums)
smallers = []
for index in range(len(nums)):
count = 0
for key, value in count_map.items():
if key < nums[index]:
count += value
smallers.append(count)
return smallers

Python - List index out of range, "for" loop

I don't get how a "for" loop that iterates through the elements of a list can be out of range.
First part seems to be okay as I can print it.
import random
def random_list(n):
l = []
for i in range(0,n):
l.append(random.randint(0,n))
return l
def maximum(n):
x = 0
b = random_list(n)
for i in b:
if b[i] > x:
x = b[i]
print (maximum(10))
This:
for i in b:
if b[i] > x:
x = b[i]
Iterates over the elements of b, not the indices. Change it to
for i in b:
if i > x:
x = i
You also need to return something from your function, probably x.
Considering that you know how to iterate over a list in python, the error could be due to randint, which generates an integer in the interval [low, high]. This means high is a possible output, while the highest index in your program is high - 1.
For example,
random.randint(0, 0)
gives 0.
Similarly, random.randint(10) can return 10.
If you don't understand how to iterate over a list in Python, consider a simple example:
Take the list below:
myList = [1, 3, 5, 7, 9]
Now, there are two ways to iterate over the list:
Directly accessing elements:
for element in myList:
print(element, end=" ")
This gives the output:
1 3 5 7 9
Accessing elements using indices
for idx in range(len(myList)):
print(idx, ":", myList[idx], end=" ")
This gives the output:
0:1 1:3 2:5 3:7 4:9

min() arg is an empty sequence

I'm trying to find minimum element in matrix row, but there are two conditions:
1) it must be > 0
2) and this point must be not visited(is_visited[k] is False)
I'm trying to do next:
min(x for x in matr_sum[i] if x > 0 if is_visited[k] is False )
But there is an error: min() arg is an empty sequence
The full block of code:
for k in range(4):
if matr_sum[i][k] == min(x for x in matr_sum[i] if x > 0 if is_visited[k] is False ) and i!=k:
return k
How to resolve it? Or should I write my min() function? Because it works with one condition:
min(x for x in matr_sum[i] if x > 0)
But with two conditions, it doesn't work.
If you want to avoid this ValueError in general, you can set a default argument to min(), that will be returned in case of an empty list. See described here.
min([], default="EMPTY")
# returns EMPTY
Note that this only works in Python 3.4+
There is no problem with the syntax. It's certainly unusual to have two if clauses, but it's allowed. Consider:
print(min(x for x in range(1,300) if x % 3 == 0 if x % 5 == 0))
Output:
15
However:
print(min(x for x in range(1,300) if x % 2 != 0 if x % 2 != 1))
Output:
ValueError: min() arg is an empty sequence
There are no integers that are both odd and even, so there are no values for min to see, so it throws an exception.
I deduce that in your code, there are no values that pass both conditions. Python doesn't allow you to compute "the minimum of no values", mainly because it makes no sense.
You have to decide what you want to do in the case where there is no minimum because there are no values greater than 0. For example, if you don't want to return k in that case then I might re-write your code something like this:
for k in range(4):
if k != i and is_visited[k] is False:
if matr_sum[i][k] > 0 and matr_sum[i][k] == min(x for x in matr_sum[i] if x > 0):
return k
It might not be obvious why this helps, but assuming matr_sum[i] is a list or similar, then once we know matr_sum[i][k] > 0 then we know the generator passed to min isn't empty, so we won't get an exception. Whereas if matr_sum[i][k] <= 0, then it certainly isn't equal to the smallest positive value, so there's no need to compute the min at all. Another way to write that would be:
if matr_sum[i][k] > 0 and not any(0 < x < matr_sum[i][k] for x in matr_sum[i])
Actually, I'd normally write if not is_visited[k], but I leave it as is False since I don't know whether changing it would change the behaviour of your code.
Try this - it creates the list of x values xs and then only tries to find the min if xs is non-empty. You may need to add some logic to handle the case that xs is empty, depending on what your code is doing.
for k in range(4):
if is_visited[k] is False and i != k:
xs = [x for x in matr_sum[i] if x > 0]
if xs and matr_sum[i][k] == min(xs):
return k
Just use and operation for concatenate tow if statement :
min(x for x in matr_sum[i] if x > 0 and if is_visited[k] is False and i!=k)

Getting index error in the code in python

def fact(n):
fac = 1
while (n>1):
fac = fac*n
n -= 1
return fac
z = 0
t = int(raw_input())
nz = []
for i in range(0,t):
c = 0
n = int(raw_input())
z = fact(n)
z = list(str(z))
for j in range(len(z)-1,1,-1):
if z[j] != '0':
break
else:
c +=1
nz[i].append(c)
for k in range(0,t):
print nz[k]
Hello I am getting
Indexerror : index out of range at " nz[i].append(c)
This program should calculate trailing zeros in the factorial of N.
Can you also please help me optimize my code, so it can run also for large values of N?
nz is empty list. It doesn't have any elements, so nz[i] would always raise IndexError. Perhaps you meant nz.append(c) ie. add c at the end of nz.
This is how does append() work:
list.append(x)
Add an item to the end of the list; equivalent to a[len(a):] = [x].
so you may want to change nz[i].append(c) to nz.append(c), since your i index is already handled by the append function. You're actually assuming you have an i element in your list, which is false, since you are using an empty list
About optimizing, your problem is probably due to your recursion limit. Try import sys; sys.getrecursionlimit() in your python shell, you should see something like 1000 as result.
Swapping for an iterative version of the factorial function could be a start
def fact(n):
r = 1
for x in range (n):
r = r * (x+1)
return r

Categories

Resources