This is my Python program:
def highest_even(li):
for num in li:
if num % 2 != 0:
li.remove(num)
return li
print(highest_even([10,2,3,4,5,80,15,99]))
And the output is :
[10, 2, 4, 80, 99]
I want to know why 99 wasn't deleted.
Thanks.
It's generally a bad idea to modify a list that you're iterating over, all sorts of strange things can happen. The particular strange thing that's happening here has to do with the way Python iterates over lists.
Think of Python maintaining the position of the current item for iterating. When you delete an item, the others all shift "left" in the list but Python still advances the position. That means, if you have two odd numbers in a row (like 15 and 99)(a), deleting the 15 moves 99 to the left (to where the 15 was) but the next iteration will be looking at where the 99 was before shifting, not where it is now.
For example, consider the list [1, 3, 6, 8], these are the steps Python will take:
List Pos newList newPos comment
---- --- ------- ------ -------
[1, 3, 6, 8] 0 [3, 6, 8] 1 Odd 1, delete, advance.
[3, 6, 8] 1 [3, 6, 8] 2 Even 6, leave, advance.
[3, 6, 8] 2 [3, 6, 8] 3 Even 8, leave, advance.
[3, 6, 8] 3 iteration finished.
You can see that consecutive odd numbers cause a problem here, it will only delete alternate ones.
As to the solution: if, as your code suggests, you just want the even numbers in the list, you can use the much more succinct (and Pythonic) list comprehension (no need even for a function to do this):
myList = [10, 2, 3, 4, 5, 80, 15, 99]
evens = [item for item in myList if item % 2 == 0]
# gives [10, 2, 4, 80]
And, for completeness, since your function name seems to indicate you want the highest even number, that would be something like:
biggestEven = max([item for item in myList if item % 2 == 0])
# gives 80
(a) Your problem actually has nothing to do with the fact the 99 is at the end of the list, any consecutive odd numbers, anywhere in the list, would cause the same issue.
do not modify a list you iterate in
you can copy before iterate on
def highest_even(li):
for num in li.copy():
if num % 2 != 0:
li.remove(num)
return li
print(highest_even([10,2,3,4,5,80,15,99]))
Execution :
[10, 2, 4, 80]
As so many comments mention: it's unsafe (or at least it presents some unexpected behavior) to modify a list as you iterate over it. The usual fix here for lists that aren't giant is simply to copy the list when you go to iterate on it.
for num in li[:]:
# everything as before
That little slice syntax makes Python take the list li, create a new list of its entire contents, and iterate over that, instead. Now since you're removing things from li, but iterating over the copy of li made by slicing it, there's no issue.
this is because of you iterated through the list while editing it.
only_even = []
for n in lst:
if not n % 2:
only_even.append(n)
other methods
only_even = [n for n in lst if not n % 2]
only_even = list(filter(lambda x: not n % 2, lst))
You should never update the data structure you are iterating in, to maintain the invariants of the loops.
If you print the li, and num right after the for num in li:, you'll see that after you remove the element from the list, the next element is skipped, meaning that the indexed is moved forward, same thing happens to 99 element.
You can check it here.
def highest_even(li):
for num in li:
print(li, num)
if num % 2 != 0:
li.remove(num)
return li
gives the output:
In [3]: highest_even([10,2,3,4,5,80,15,99])
([10, 2, 3, 4, 5, 80, 15, 99], 10)
([10, 2, 3, 4, 5, 80, 15, 99], 2)
([10, 2, 3, 4, 5, 80, 15, 99], 3)
([10, 2, 4, 5, 80, 15, 99], 5)
([10, 2, 4, 80, 15, 99], 15)
Out[3]: [10, 2, 4, 80, 99]
You should not iterate through a list and delete the elements of the same list.
Since the index is used iterate in a loop.
0- 10
1- 2
2- 3
3- 4
4- 5
5- 80
6- 15
7- 99
And as when you delete the elements from list it skips the next element.
In your example, for the indexes 0 & 1 nothing changes. But when index =3 and as per the condition this element is removed and the list would be updated to [10,2,4,5,80,15,99].
After index =3 the next index is 4 and li[4] equals 5 and not 4. And your condition is not even checked for the element 4. It just happened to be even to be correct. Having some odd instead of 4 would again give you wrong output.
Same is the case with the last element 99. Since the previous element 15 or index = 6 is removed the length of the list reduced by 1 and it does not loop for index = 4 as index reached its max value of updated list is 5(after removing 3,5 & 15).
You shouldn't update a list while iterating over it. But you can make it work by going backwards or you will be cutting a tree branch while sitting on it.
li = [10,2,3,4,5,80,15,99]
for i in range(len(li) - 1, -1, -1):
if (i%2 != 0 ):
del li[i]
print(li)
What's happening there is that you are changing a list while you iterate over it, but the iterator on that list will not get updated with your changes.
you can try this:
for i in range len(li):
Related
I am still a newbie in programming and I need some help
I want to substract 9 from numbers bigger then 9 from a list.
ls = [1, 2, 3, 4 ,5, 6, 7, 8, 9]
odd_num = []
for _ in ls[::2]:
odd_num.append(_)
ls.remove(_)
for _ in odd_num:
multip_elem = _ * 2
ls.append(multip_elem)
for _ in ls:
if _ > 9:
substraction = _ - 9
ls.append(substraction)
ls.remove(_)
print(ls)
This is the output
[2, 4, 6, 8, 2, 6, 14, 1, 9]
14 is still in the list but I am pretty sure is bigger then 9 :))
ls = [1, 2, 3, 4 ,5, 6, 7, 8, 9, 10, 11]
ls = [i-9 if i>9 else i for i in ls]
print(ls)
A pythonic way of turning one list into other.
It uses a mechanism called list comprehension.
I have skipped the part for odd numbers, to simplify it.
This approach first creates a new list, and then assigns it to the same variable.
Note it is generally not a good idea to modify a list while iterating it (adding and removing items).
You should not modify a list while iterating on it, this always causes problems ;)
You'd better iterate over it using it's indices, and replace the value directly
for i in range(len(ls)):
if ls[i] > 9:
ls[i] -= 9
Or use a list comprehension to create a new list from ls:
ls = [i-9 if i>9 else i for i in ls]
There are better answers above (#Mirronelli for instance), but I tried to keep this answer as close as possible to your original code, in case you needed to keep a similar structure.
ls = [1, 2, 3, 4 ,5, 6, 7, 8, 9]
odd_num = []
for element in ls:
# Separate list into odd and even numbers
if element % 2 != 0:
# Create your odd list
odd_num.append(element)
ls.remove(element)
# You don't need to do append here
# Just use a list comprehension
odd_num = [element * 2 for element in odd_num]
# Again, you don't need append/remove here
# Just use a list comprehension
ls = [element for element in ls if element < 9]
print(ls)
I have a list of numbers. Assume it is a random selection of numbers in the range of 40.
I have a for loop that selects one index in each iteration. after finishing one round in the loop, I save the index I selected in a list and then delete it from the original list (so after each iteration one element is deleted from the original list).
perm = random.sample(range(1, 40), 30)
for i in range(7):
index = random.randrange(len(perm))
perm.pop(index)
In the end, I have a list of indices showing the number of the index I deleted at each iteration say something like l = [5, 15, 6, 7, 11, 3, 8]. Is there an easy way to get the original place of these indices? (for example I deleted 5 first, so when I selected 15 it was actually in the 16th place in the original array and I want to find the original place of all indices)
I tried something simple like this:
for i in range(len(l)):
for j in range(i):
if l[i] >= l[j]:
l[i] += 1
But it does not seem to be quite right. Any ideas on how to make it work with just one loop?
l = [5, 15, 6, 7, 11, 3, 8]
copy = l[:]
for i in range(len(l)):
for j in range(i):
l[i] += 1 if copy[i] >= copy[j] else 0
print(l) # [5, 16, 7, 9, 14, 3, 12]
Like #pavel said, you need to make a working copy. Then your idea works fine.
I need to perform a triple cut. My function takes as parameter list of int, and somewhere in that list there is 27 and 28. What I need to do is to check what comes first 27 or 28, everything before the 27 or 28(depending upon what comes first) goes to the bottom of the list and everything after 27 or 28 (depending upon what comes second will go the top of the list.
Here is an example:
>>> test_list = [1, 28, 3, 4, 27, 5, 6, 7, 8]
>>> triple_cut(test_list)
>>> test_list = [5, 6, 7, 8, 28, 3, 4, 27, 1]
Here is what I have so far
#find the index positions of the two jokers
find_joker1 = deck.index(27)
find_joker2 = deck.index(28)
print(find_joker1, find_joker2)
new_list = []
# use selection to check which joker occurs first
if(find_joker1 > find_joker2): # joker2(28) occurs first in the list
# loop throgh each element in the list that occurs before Joker1
# and move them to the end of the list
# move element that occur after Joker1(27) to the top of the list
for i in deck:
if(deck.index(i) > find_joker1): # elements that occur after second joker
new_list.append(i) # move those element to the top of the list
new_list.append(28) # add Joker2
for i in deck: # element between the two Jokers
if(deck.index(i) > find_joker2 and deck.index(i) < find_joker1):
new_list.append(i)
new_list.append(27)
for i in deck: # elements before the first joker
if(deck.index(i) < find_joker2):
new_list.append(i)
print(new_list)
Can be solved by slicing.
def triple_cut(lst):
a=lst.index(27)
b=lst.index(28)
if a>b:
return lst[a+1:]+ lst[b:a+1]+ lst[:b]
else:
return lst[b+1:]+ lst[a:b+1]+ lst[:a]
What actually happenning:
Slice everything after the bigger indexed one.
Slice from the lower indexed one to upper indexed one.
Slice everything before the lower indexed one.
Add all together.
N.B: During slicing, first index is inclusive and second index is exclusive.
Demo,
>>test_list = [1, 28, 3, 4, 27, 5, 6, 7, 8]
>>tripplecut(test_list)
[5, 6, 7, 8, 27, 3, 4, 28, 1]
Some explanation:
By slicing, you can get a part of a list. First see, how actually slicing works:
lst[start:end:increment]
For relevance to your question, skip the increment part. Default increment is 1, just what we need. So, we will slice a list like below:
lst[start:end]
Let's do some experiments with your given list.
>>> test_list = [1, 28, 3, 4, 27, 5, 6, 7, 8]
Say, we want a list from index 2(3) to index 5(27). Simply do:
>>> test_list[2:6]
[3,4,27]
Why I've used 6 in place of 5. That's because:
In case of slicing, the start index is inclusive, but the end index is exclusive.
What if we want a list from start to index 4(27). Do:
>> test_list[:5]
[1,28,3,4,27]
If we want index 3 to end? Simply do:
>>test_list[3:]
[4, 27, 5, 6, 7, 8]
Hope that will help you a bit.
Since you don't actually need to know which is which, you can just find the two indexes and slice.
def triple_cut(lst):
first, second = [i for i, e in enumerate(lst) if e in [27, 28]]
return lst[second+1:] + lst[first:second+1]+ lst[:first]
Bit of a generic noob question. I am heavily dealing with long lists of integer/float values and wasting a lot of time.
my_list = [1,2,3,4,5,6,7,8,9,10.....] etc.
Say I want to pass a portion of that list to a function. It could be the first 3 elements, then the following 3 etc....it could also be in groups of 4,5,6....it might even be required that I take different a different amount of elements each time.
def myfunc(x,y,z):
do something
return something
What is the most efficient way to iterate by a specified number of values, as efficiency is always appreciated and these simple iterations are the places where I can gain something.
len_ml = len(my_list)
for i in range(0, len_ml, 3):
chunk = my_list[i:min(len_ml, i+3)]
This is a way. I am not sure that it is the best, though.
With a list you can get just the items you want with list[start:end].
So to skip the first list[1:] or last list[:-1] or first 3 and last 3 list[3:-3]
if you don't know how may items are in the list to start you can do a len(list) to get number. So if you had X items and wanted 3 groups :
numberofgroups = len(list) / 3
To do for loop over only certain ones:
start_index=1
end_index=-1
for item in my_list[start_index:end_index]
print item
>>>my_list = [1,2,3,4,5,6,7,8,9,10]
>>>group_len = 3 #you can change the length as per your requirement i.e 2,3,4,5,6...
>>>for i in range(0,len(my_list)):
if i*group_len < len(my_list):
my_list[i*group_len:(i+1)*group_len]
else:
break;
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
Result for group_len = 5
[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
I have the following python code:
x = range(0,10)
print x
for number in x:
print(number)
if number%2<> 0:
x.remove(number)
print x
Oddly, the out put is this:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0
1
3
5
7
9
[0, 2, 4, 6, 8]
The first and last lines are right, but why are 2,4,6, and 8 not printed out? The print statement is not inside the if statement!
I'm using python(x,y) on windows 7. Also, I'm new to Python...I'm used to C++
You're removing items from the list (x.remove) while iterating over it (for number in x).
for-in maintains an index separately, and that is why modifying the list gives unexpected behavior.
The list is iterated using its index, but when you remove elements you skip some indices.
E.g:
[0,1,2,...] # (iterator is at second - print 1)
remove
[0,2,3,...] # (iterator is still at second)
iterator advances
[0,2,3,...] # (iterator is at third - print 3)
Add some print statements for clarity:
x = range(10)
for index, number in enumerate(x):
print "x is ", x
print "element is", number
print "index is ", index
print
if number % 2 == 0:
x.remove(number)
And the output:
x is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
element is 0
index is 0
x is [1, 2, 3, 4, 5, 6, 7, 8, 9]
element is 2
index is 1
x is [1, 3, 4, 5, 6, 7, 8, 9]
element is 4
index is 2
x is [1, 3, 5, 6, 7, 8, 9]
element is 6
index is 3
x is [1, 3, 5, 7, 8, 9]
element is 8
index is 4
As you can see, index keeps going up by 1, even though you remove elements from the list. This is what causes the loop to skip elements.
As others have pointed out, looping over a list and removing elements from it isn't a good idea. Loop over a copy instead:
for number in x[:]:
Or:
for number in list(x):
Better yet, make a new list with a list comprehension:
[number for number in x if number % 2 == 0]
Basically you can have weird behavior when you iterate something while removing at the same time. What's happening is that you're skipping some values due to them being shifted to indexes that you already iterated over.
A better way of doing what you want (filter out some items), would be to use a list comprehension, for instance:
[x for x in range(10) if x%2==0]
You could simply use the range step to only create even numbers, but the above solution let's you filter out on any condition.
The reason why some numbers aren't printed is that the values are changing positions while you loop and remove them. When you remove the 1, you can imagine all the values being shifted by one position, the iterator is pointing to where the 2 used to be, but now the value there is 3, so the 2 is never printed. And this goes on for the rest of the values.
As Mark Rushakoff mentions, you shouldn't modify something while you're iterating over it. Change your for number in x to for number in x[:] and it will work as you expect, though. In this case you're iterating over a copy.
Don't modify a list you're iterating over. Others suggest copying the list or collecting a new list of things to remove. Instead, collect the ones you want to remain. This is faster than copying the list and removing from the copy not being iterated over, and faster than collecting the ones to remove and then removing them.
evens = []
for number in x:
if number%2 == 0:
evens += [number]
print(evens)