I am getting confused with an implementation of a loop in python while working with lists.
example: This implementation always throws list index out of range error because the list is different in each iteration and the len(intervals) always changes.
for i in range(len(intervals1)):
for k in range(len(intervals2)):
if intervals1[i] == intervals2[k]:
count += 1
else:
intervals2.pop(k)
while the second implementation is
for i in intervals1:
for k in intervals2:
if i == k:
count += 1
else:
intervals2.remove(k)
The second implementation works fine, but the first one always fails. I think that we can never work with an indexed approach while using for loops when we are removing/popping something from a list or modifying the number of elements in the list.
Could somebody please provide a workaround if we want to use indexed approach with the first implementation.
One implementation I have found is by using while loops, why does this work even though it is using an indexed based approach but for loops fail-
intervals1 = [1,2,3,4,5,6]
intervals2 = [4,5,6,7,8,9]
i=0
while i < len(intervals2):
if intervals1[i] == intervals2[i]:
count +=1
else:
intervals2.pop(i)
intervals1.pop(i)
print(intervals1)
print(intervals2)
Shouldn't this also fail as it is dynamically computing the length of the intervals. Maybe the reason is it isn't using a counter like for loop does, it can use that inside the loop though.
Could someone please explain this.
Thanks.
Instead of pop-out elements, it is better to store the desired one in a new list popping out will cause this index error
intervals1 = [1,2,3,4,5,6]
count=0
intervals2 = [4,5,6,7,8,9]
res=[]
for i in range(len(intervals1)):
for k in range(len(intervals2)):
if intervals1[i] == intervals2[k]:
count += 1
res.append(intervals2[k])
Related
Found that the control never reaches the inner while loop in this snippet for deleting a duplicate number
numbers=[1,6,6,7]
k=len(numbers)
i=0
j=0
while i in range(k-1):
while j in range(i+1,k):
if numbers[i] == numbers[j]:
numbers.remove(numbers[j])
k-=1
j-=1
j += 1
i += 1
print(numbers)
Your code does not make j start at i+1. Instead it starts at zero and never changes. The inner loop never runs because 0 is outside of the range you are testing.
Try this simple change:
i=0
while i < k+1:
j=i+1
while j < k:
if numbers[i] == numbers[j]:
...
The main change is moving the initialization of j inside the first while loop, so it updates each time you go through it, and never starts out less than or equal to i.
The other change I made is much less important. Rather than using i in range(...) and j in range(...) for the while loop conditions, I just did an inequality test. This is exactly the same as what the range membership test does under the covers, but avoids unnecessary testing for things that can't happen (like j being too small, now). It also makes the loop look a lot less like a for loop, which uses for i in range(...) syntax a lot (with a different meaning).
Another issue you may run into later, with some sets with multiple sets of duplicates is that your code to remove the jth element probably doesn't do what you intend. The call numbers.remove(numbers[j]) removes the first value equal to numbers[j] from the list, which is going to be the one at index i rather than the one at index j. To delete a list item by index, you want to use del numbers[j].
It doesn't reach because j and i starts at 0 value and in the inner while loop the condition is j in range(i+1, k) which means range(1, 4) and 0 in range(1, 4) would be False. Anyways, you should avoid using j and i as counters and use a for loop instead.
But the solution is easier and doesn't need to traverse the list, if you wanna remove the duplicate values, you can do as below:
numbers = [1, 6, 6, 7]
print(list(set(numbers)))
The result is: [1, 6, 7]
You could remove duplicates from a list in Python by using the dict.fromkeys().
numbers=[1,6,6,7]
final_numbers = list(dict.fromkeys(numbers))
print(final_numbers)
In this example, we use the dict.fromkeys() method to create a dictionary from numbers variable. We then use list() to convert our data from a dictionary back to a list. Then, on the final line, we print out our revised list.
Another option is to use set.
Sets are used to store collections of unique items in Python. Unlike lists, sets cannot store duplicate values.
We can convert our list to a set to remove duplicated items.
numbers=[1,6,6,7]
final_numbers = list(set(numbers))
print(final_numbers)
I want to use a for loop to define a range and I would like to use a while loop to check a condition for every value inside this for loop and give me the results for different values of c, but unfortunately, my algorithm doesn't work and I don't know what my mistake is.
j=0
jl=[]
c=np.linspace(0,20,num=20)
for a in range(0,len(c)):
while j<5:
j=c[a]+2
jl.append(j)
The result I am looking for is it puts different values of c inside the while loop and calculate j and check if it is bigger than 5 or not. if yes, it appends it to jl. Totally, I want to define a range with for loop with index and also check each value of this range inside while loop and get the results for j.
so expected results are j with values smaller than 5 (c[a]+2<5) and store the values of j in jl
There is one problem in your code:
j=0
"While loop" never runs because his condition is j>5. Correct that and tell us if it works.
Please double check if the following suggested solution covers all of your requirements. In any case I think List Comprehensions (e.g. compare section 5.1.3.) can help you.
c=np.linspace(0,20,num=20)
ls = [x+2 for x in c if((x+2)<5)]
print(ls)
Will result in the following output:
[2.0, 3.052631578947368, 4.105263157894736]
If you want to do more complex data manipulation, you can also do this with the help of functions, e.g. like:
def someManipulation(x):
return x+2
ls = [someManipulation(x) for x in c if(someManipulation(x)<5)]
Your algorithm doesn't work because
while j<5:
j=c[a]+2
is an infinite loop for j = 0, a = 0.
What you probably wanted to write was:
for x in c:
j = x + 2
if j < 5:
jl.append(j)
Still, the list comprehension version in the other answer does exactly the same, but better.
I am building a function to extract all negatives from a list called xs and I need it to add those extracted numbers into another list called new_home. I have come up with a code that I believe should work, however; it is only showing an empty list.
Example input/output:
xs=[1,2,3,4,0,-1,-2,-3,-4] ---> new_home=[1,2,3,4,0]
Here is my code that returns an empty list:
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if num <0:
new_home= new_home+ xs.pop(num)
return
return new_home
Why not use
[v for v in xs if v >= 0]
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if xs[num] < 0:
new_home.append(xs[num])
return new_home
for your code
But the Chuancong Gao solution is better:
def extract_negative(xs):
return [v for v in xs if v >= 0]
helper function filter could also help. Your function actually is
new_home = filter(lambda x: x>=0, xs)
Inside the loop of your code, the num variable doesn't really store the value of the list as you expect. The loop just iterates for len(xs) times and passes the current iteration number to num variable.
To access the list elements using loop, you should construct loop in a different fashion like this:
for element in list_name:
print element #prints all element.
To achieve your goal, you should do something like this:
another_list=[]
for element in list_name:
if(element<0): #only works for elements less than zero
another_list.append(element) #appends all negative element to another_list
Fortunately (or unfortunately, depending on how you look at it) you aren't examining the numbers in the list (xs[num]), you are examining the indexes (num). This in turn is because as a Python beginner you probably nobody haven't yet learned that there are typically easier ways to iterate over lists in Python.
This is a good (or bad, depending on how you look at it) thing, because had your code taken that branch you would have seen an exception occurring when you attempted to add a number to a list - though I agree the way you attempt it seems natural in English. Lists have an append method to put new elements o the end, and + is reserved for adding two lists together.
Fortunately ignorance is curable. I've recast your code a bit to show you how you might have written it:
def extract_negatives(xs):
out_list = []
for elmt in xs:
if elmt < 0:
out_list.append(elmt)
return out_list
As #ChuangongGoa suggests with his rather terse but correct answer, a list comprehension such as he uses is a much better way to perform simple operations of this type.
How can I update the upper limit of a loop in each iteration? In the following code, List is shortened in each loop. However, the lenList in the for, in loop is not, even though I defined lenList as global. Any ideas how to solve this? (I'm using Python 2.sthg)
Thanks!
def similarity(List):
import difflib
lenList = len(List)
for i in range(1,lenList):
import numpy as np
global lenList
a = List[i]
idx = [difflib.SequenceMatcher(None, a, x).ratio() for x in List]
z = idx > .9
del List[z]
lenList = len(List)
X = ['jim','jimmy','luke','john','jake','matt','steve','tj','pat','chad','don']
similarity(X)
Looping over indices is bad practice in python. You may be able to accomplish what you want like this though (edited for comments):
def similarity(alist):
position = 0
while position < len(alist):
item = alist[position]
position += 1
# code here that modifies alist
A list will evaluate True if it has any entries, or False when it is empty. In this way you can consume a list that may grow during the manipulation of its items.
Additionally, if you absolutely have to have indices, you can get those as well:
for idx, item in enumerate(alist):
# code here, where items are actual list entries, and
# idx is the 0-based index of the item in the list.
In ... 3.x (I believe) you can even pass an optional parameter to enumerate to control the starting value of idx.
The issue here is that range() is only evaluated once at the start of the loop and produces a range generator (or list in 2.x) at that time. You can't then change the range. Not to mention that numbers and immutable, so you are assigning a new value to lenList, but that wouldn't affect any uses of it.
The best solution is to change the way your algorithm works not to rely on this behaviour.
The range is an object which is constructed before the first iteration of your loop, so you are iterating over the values in that object. You would instead need to use a while loop, although as Lattyware and g.d.d.c point out, it would not be very Pythonic.
What you are effectively looping on in the above code is a list which got generated in the first iteration itself.
You could have as well written the above as
li = range(1,lenList)
for i in li:
... your code ...
Changing lenList after li has been created has no effect on li
This problem will become quite a lot easier with one small modification to how your function works: instead of removing similar items from the existing list, create and return a new one with those items omitted.
For the specific case of just removing similarities to the first item, this simplifies down quite a bit, and removes the need to involve Numpy's fancy indexing (which you weren't actually using anyway, because of a missing call to np.array):
import difflib
def similarity(lst):
a = lst[0]
return [a] + \
[x for x in lst[1:] if difflib.SequenceMatcher(None, a, x).ratio() > .9]
From this basis, repeating it for every item in the list can be done recursively - you need to pass the list comprehension at the end back into similarity, and deal with receiving an empty list:
def similarity(lst):
if not lst:
return []
a = lst[0]
return [a] + similarity(
[x for x in lst[1:] if difflib.SequenceMatcher(None, a, x).ratio() > .9])
Also note that importing inside a function, and naming a variable list (shadowing the built-in list) are both practices worth avoiding, since they can make your code harder to follow.
I have written a simple python program
l=[1,2,3,0,0,1]
for i in range(0,len(l)):
if l[i]==0:
l.pop(i)
This gives me error 'list index out of range' on line if l[i]==0:
After debugging I could figure out that i is getting incremented and list is getting reduced.
However, I have loop termination condition i < len(l). Then why I am getting such error?
You are reducing the length of your list l as you iterate over it, so as you approach the end of your indices in the range statement, some of those indices are no longer valid.
It looks like what you want to do is:
l = [x for x in l if x != 0]
which will return a copy of l without any of the elements that were zero (that operation is called a list comprehension, by the way). You could even shorten that last part to just if x, since non-zero numbers evaluate to True.
There is no such thing as a loop termination condition of i < len(l), in the way you've written the code, because len(l) is precalculated before the loop, not re-evaluated on each iteration. You could write it in such a way, however:
i = 0
while i < len(l):
if l[i] == 0:
l.pop(i)
else:
i += 1
The expression len(l) is evaluated only one time, at the moment the range() builtin is evaluated. The range object constructed at that time does not change; it can't possibly know anything about the object l.
P.S. l is a lousy name for a value! It looks like the numeral 1, or the capital letter I.
You're changing the size of the list while iterating over it, which is probably not what you want and is the cause of your error.
Edit: As others have answered and commented, list comprehensions are better as a first choice and especially so in response to this question. I offered this as an alternative for that reason, and while not the best answer, it still solves the problem.
So on that note, you could also use filter, which allows you to call a function to evaluate the items in the list you don't want.
Example:
>>> l = [1,2,3,0,0,1]
>>> filter(lambda x: x > 0, l)
[1, 2, 3]
Live and learn. Simple is better, except when you need things to be complex.
What Mark Rushakoff said is true, but if you iterate in the opposite direction, it is possible to remove elements from the list in the for-loop as well. E.g.,
x = [1,2,3,0,0,1]
for i in range(len(x)-1, -1, -1):
if x[i] == 0:
x.pop(i)
It's like a tall building that falls from top to bottom: even if it is in the middle of collapse, you still can "enter" into it and visit yet-to-be-collapsed floors.
I think the best way to solve this problem is:
l = [1, 2, 3, 0, 0, 1]
while 0 in l:
l.remove(0)
Instead of iterating over list I remove 0 until there aren't any 0 in list
List comprehension will lead you to a solution.
But the right way to copy a object in python is using python module copy - Shallow and deep copy operations.
l=[1,2,3,0,0,1]
for i in range(0,len(l)):
if l[i]==0:
l.pop(i)
If instead of this,
import copy
l=[1,2,3,0,0,1]
duplicate_l = copy.copy(l)
for i in range(0,len(l)):
if l[i]==0:
m.remove(i)
l = m
Then, your own code would have worked.
But for optimization, list comprehension is a good solution.
The problem was that you attempted to modify the list you were referencing within the loop that used the list len(). When you remove the item from the list, then the new len() is calculated on the next loop.
For example, after the first run, when you removed (i) using l.pop(i), that happened successfully but on the next loop the length of the list has changed so all index numbers have been shifted. To a certain point the loop attempts to run over a shorted list throwing the error.
Doing this outside the loop works, however it would be better to build and new list by first declaring and empty list before the loop, and later within the loop append everything you want to keep to the new list.
For those of you who may have come to the same problem.
I am using python 3.3.5. The above solution of using while loop did not work for me. Even if i put print (i) after len(l) it gave me an error. I ran the same code in command line (shell)[ window that pops up when we run a function] it runs without error. What i did was calculated len(l) outside the function in main program and passed the length as a parameter. It worked. Python is weird sometimes.
I think most solutions talk here about List Comprehension, but if you'd like to perform in place deletion and keep the space complexity to O(1); The solution is:
i = 0
for j in range(len(arr)):
if (arr[j] != 0):
arr[i] = arr[j]
i +=1
arr = arr[:i]
x=[]
x = [int(i) for i in input().split()]
i = 0
while i < len(x):
print(x[i])
if(x[i]%5)==0:
del x[i]
else:
i += 1
print(*x)
Code:
while True:
n += 1
try:
DATA[n]['message']['text']
except:
key = DATA[n-1]['message']['text']
break
Console :
Traceback (most recent call last):
File "botnet.py", line 82, in <module>
key =DATA[n-1]['message']['text']
IndexError: list index out of range
I recently had a similar problem and I found that I need to decrease the list index by one.
So instead of:
if l[i]==0:
You can try:
if l[i-1]==0:
Because the list indices start at 0 and your range will go just one above that.