For Loop, why index out of range? - python

Write a function named same_values() that takes two lists of numbers of equal size as parameters.
The function should return a list of the indices where the values were equal in lst1 and lst2.
For example, the following code should return
[0, 2, 3]
same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5])
My original attempt:
def same_values(lst1, lst2):
new_list=[]
for i in lst1:
for j in lst2:
if lst1[i] == lst2[j]:
new_list.append(lst1[i])
return new_list
I looked up the solution which is:
def same_values(lst1, lst2):
new_lst = []
for index in range(len(lst1)):
if lst1[index] == lst2[index]:
new_lst.append(index)
return new_lst
yet I wonder why my original attempt is not valid (it says list index out of range which I don't get in this case). Thanks everyone for helping me out!

Just try:
lst1 = [5, 1, -10, 3, 3]
for i in lst1:
print(i)
You are actually iterating over the values in the list, not the indexes.

In the below condition i is considered as array value. so i would be 5,1,-1, which are invalid indexes
def same_values(lst1, lst2):
new_list=[]
for i in lst1:
for j in lst2:
if lst1[i] == lst2[j]:
new_list.append(lst1[i])
return new_list

You have 2 major issues (i.e. go against the problem statement) with your logic:
You are comparing every value in list 1 to every value in list 2. So, if you the following lists [10,1,10,1,10] and [1,10,1,10,1] your code would identify all 5 position as positive hits
Instead of recording the index or position where you get the a positive hit, you are recording the corresponding value from list 1. In your example, instead of [0,2,3] you would get [5,-10,3]
That being said, have you actually tried running your code? It would be very helpful to you if you actually debugged your code and stepped through each operation.

Related

Multiplying only odd indexes in a for loop

Multiplying only odd indexes with for loop
Why its not working?
myList = [1, 2, 3, 4, 5]
index = 0
for num in myList:
if index % 2 != 0:
num *= 2
index += 1
print(myList) # here the output still the same list
I want the output to be [1, 4, 3, 8, 5]
Edit: I see that you have edited your question, where you fixed the first issue of checking if the items in the list are odd or not. You are however still iterating over the list using for num in myList, which under the hood creates an Iterator which moves over your list. This means that whatever you do with num, you are only modifying num and not myList[index]. In order to modify the list directly, you will need to reference myList[index]. I strongly recommend you look into using enumerate, see my original answer for how to apply it to your use-case.
Your problem is that you are checking if the value inside myList is even or odd instead of its index.
Also, modifying num within the loop does not modify the value in your original list (this can easily be noticed since in the "modified" list the odd values are not multiplied).
Using the range(len()) idiom to loop over your list would yield the following code:
myList = [1, 2, 3, 4, 5]
for idx in range(len(myList)):
if idx % 2 != 0:
myList[idx] *= 2
print(myList)
You could further shorten the loop/assignment by using enumerate and list comprehension:
myList = [1, 2, 3, 4, 5]
myList = [num * 2 if idx % 2 != 0 else num for idx, num in enumerate(myList)]
print(myList)
Your way isn't working because you are not assigning 'num' back into the list.
newList = [v*2 if idx % 2 != 0 else v for idx, v in enumerate(myList)]
There are two issues in the code -
You need to check if the index is odd or even, and not the element.
Modifying num won't modify the corresponding element in the list as it does not reference the element in the list.
myList = [1, 2, 3, 4, 5]
for idx in range(len(myList)):
if idx % 2 != 0:
myList[idx]*=2

Find index of minimum value in a Python sublist - min() returns index of minimum value in list

I've been working on implementing common sorting algorithms into Python, and whilst working on selection sort I ran into a problem finding the minimum value of a sublist and swapping it with the first value of the sublist, which from my testing appears to be due to a problem with how I am using min() in my program.
Here is my code:
def selection_sort(li):
for i in range(0, len(li)):
a, b = i, li.index(min(li[i:]))
li[a], li[b] = li[b], li[a]
This works fine for lists that have zero duplicate elements within them:
>>> selection_sort([9,8,7,6,5,4,3,2,1])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
However, it completely fails when there are duplicate elements within the list.
>>> selection_sort([9,8,8,7,6,6,5,5,5,4,2,1,1])
[8, 8, 7, 6, 6, 5, 5, 5, 4, 2, 9, 1, 1]
I tried to solve this problem by examining what min() is doing on line 3 of my code, and found that min() returns the index value of the smallest element inside the sublist as intended, but the index is of the element within the larger list rather than of the sublist, which I hope this experimentation helps to illustrate more clearly:
>>> a = [1,2,1,1,2]
>>> min(a)
1 # expected
>>> a.index(min(a))
0 # also expected
>>> a.index(min(a[1:]))
0 # should be 1?
I'm not sure what is causing this behaviour; it could be possible to copy li[i:] into a temporary variable b and then do b.index(min(b)), but copying li[i:] into b for each loop might require a lot of memory, and selection sort is an in-place algorithm so I am uncertain as to whether this approach is ideal.
You're not quite getting the concept correctly!
li.index(item) will return the first appearance of that item in the list li.
What you should do instead is if you're finding the minimum element in the sublist, search for that element in the sublist as well instead of searching it in the whole list. Also when searching in the sliced list, you will get the index in respect to the sublist. Though you can easily fix that by adding the starting step to the index returned.
A small fix for your problem would be:
def selection_sort(li):
for i in range(0, len(li)):
a, b = i, i + li[i:].index(min(li[i:]))
li[a], li[b] = li[b], li[a]
When you write a.index(min(a[1:])) you are searching for the first occurence of the min of a[1:], but you are searching in the original list. That's why you get 0 as a result.
By the way, the function you are looking for is generally called argmin. It is not contained in pure python, but numpy module has it.
One way you can do it is using list comprehension:
idxs = [i for i, val in enumerate(a) if val == min(a)]
Or even better, write your own code, which is faster asymptotically:
idxs = []
minval = None
for i, val in enumerate(a):
if minval is None or minval > val:
idxs = [i]
minval = val
elif minval == val:
idxs.append(i)

Making a new list that returns the minimum value digits' position/index from the previous list

Make a function called positions_lowest(lorig1), with at least one element, with integer numbers, possibly repeated, and returns a new list (lpos) containing the positions where the minimum number is in the original list.
For example:
lorig=[10,2,-3,34.5, 22,-3,1]
lres = positions_lowest(lorig)
print(lres)
# gives the output:
[2, 5] # because the minimum digit is -3 and positions are 2 and 5
I tried many times fixing my code and it is becoming more and more complicated for this simple question, below is my code. It does not even execute
def positions_lowest(lorig):
lpos = []
if lorig:
min_val = lorig[0]
[(i,j) for i,val in enumerate(lorig) if j < min_val]
if j == min_j:
lpos.append(i)
else:
min_val = j
lpos = [i]
return lpos
Try to split the problem into two steps - finding the minimum element and then returning all indices of the minimum element. This will make the problem easier in my opinion. There is a built-in function for the first step - min:
In [1]: a = [10, 2, -3, 34.5, 22, -3, 1]
In [2]: minv = min(a)
# minv is now -3
Now try using a list comprehension to get all indices of minv. I think you are on the right path. You should use enumerate over the list (to be able to get the indices) and then compare each value to minv.
This is not my answer, its from https://stackoverflow.com/a/15098701/12128167
You will have to make changes if you want different variable names or formats but this is
sweet and simple.
a = [10,2,-3,34.5, 22,-3,1]
def locate_min(a):
smallest = min(a)
return smallest, [index for index, element in enumerate(a)
if smallest == element]
print(locate_min(a))
Output:
(-3, [2, 5])
where -3 is the minimum number and the list of elements shows the index of the minimum number.
IF YOU DON'T WANT MINIMUM
return [index for index, element in enumerate(a)
if smallest == element]
Change code like above.
Gives you:
[2, 5]

List index out of range even when loops don't iterate on the lists

I'm a beginner at python coding and I've been trying to do this exercise to remove all adjacent characters from a list of integers to a single element using the following block of code:
#nums is a list of integers with repeating adjacent characters. For eg. [1,2,2,2,3,3]
length = len(nums)
for i in range(length):
while nums[i] == nums[i+1]:
del(nums[i+1])
length-=1
I keep getting the list index out of range error on the while loop, but since neither of the loops depends on the indices of nums directly, then how am I violating the bounds of the list?
I have previously checked with other answers to the same problem which all have solutions that relate to list comprehension which I don't want to apply to my code. Also, I'm aware of other approaches to removing adjacent elements but I would like to know what was wrong with my bit of code here for learning purposes. Thanks in advance.
I've added a check for confirming length. Is this what you want? It simply executes the loop if items are available.
nums = [1, 2, 2, 2, 3, 3]
length = len(nums)
for i in range(length):
while i < length-1 and nums[i] == nums[i + 1] :
del (nums[i + 1])
length -= 1
print(nums)
There are 2 errors here.
You are iterating over a range which cannot be indexed; in this case, the nums[len(nums)] does not exist. Remember Python indexing begins at 0.
You are modifying your list as you iterate. Don't do this, as the length of your list will change and your iteration will not work as intended.
Here is some code that works:
nums = [1,2,2,2,3,3]
length = len(nums)
items = []
for i in range(length-1):
if nums[i] == nums[i+1]:
items.append(i+1)
res = [i for i, j in enumerate(nums) if i not in items]
# [1, 2, 3]
(Python 3.6)
If you want remove all duplicate and keep the ordering:
>>> a = [1, 1, 2, 2, 2, 3, 4, 6, 3 ]
>>> list(dict.fromkeys(a))
[1, 2, 3, 4, 6]
But it will remove the last 3 too.
You can track array from end to start by length-1 like this:
nums = [1,2,2,2,3,3]
length = len(nums)
for i in reversed(range(length-1)):
while (nums[i] == nums[i-1]):
del(nums[i])
print(nums)

python:two programs to delete val from nums

write a python program to delete val from array:
the first program is:
class Solution(object):
def removeElement(self,nums,val):
for x in nums:
if x == val:
nums.remove(val)
return len(nums)
when the nums is [3,3], the val is 3, the output is :1
the second program is :
class Solution(object):
def removeElement(self,nums,val):
while val in nums:
nums.remove(val)
return len(nums)
the nums is [3,3],the val is 3,the output is :0
could you please tell me the difference and reason
Python has a hard time iterating through something that is being changed during the iteration. It might fail or return unexpected results or never exit the iteration.
Because a container doesn't even keep track of iterators that are out
on it, much less hook even altering-method to loop over every such
iterator and somehow magically let each iterator know about the
alterations. It would be a lot subtle, complex code, and checks
slowing down very frequent operations.
nums=[3,4]
val=3
for x in nums:
print(x,nums)
if x > val:
nums.append(4)
The above code will not exit the iteration.So the correct way is to use a list comprehension to create a new list containing only the elements you don't want to remove:
print([i for i in nums if i!=val])
Or in-place alteration:
nums[:] = [i for i in nums if i!=val]
Hope this helps.
In the first example you are deleting an element inside the list while iterating over the elements of the list. That's a problem because you will run out of iterations after the first remove.
If the list is, for example, l = [1, 1, 2, 2] and you call removeElement(l, 2) after the first 2 is deleted, the length of l will be 3 and there won't be any iterations left (the list will be [1, 1, 2] and you will be at iteration number 3, that's why the loop stops and you get returned [1, 1, 2]).
In the second case, the while statement is: while there is still a 2 in the list, continue removing 2 from the list. This way, after the first iteration the l will look like [1, 1, 2] and there would still be a 2 inside it, so the loop continues and you get [1, 1].
You can apply this example on your case with l = [3, 3] and see if you understand it.
Hope that helped :)

Categories