List index out of range using While loop - python

This is my first time posting and I am a bit of newbie so please excuse any errors in my question. I have to create a function that uses the While loop and takes in a list of numbers and adds each number to a new list until the a specific number is reached. For example - I want each number in a list to be added until the number 5 is reached in the list. If the number 5 is not in the list, then I would get the entire list as is. The issue I am having is with this last part. My current code, posted below, can give me a new list of numbers that stops at the number 5 but I get the "List index out of range" error when the number 5 is not included in the list. I am sure its something small that I am not factoring in. Any help or guidance on what I am doing wrong would be greatly appreciated.
def sublist(x):
i = 0
num_list = []
while x[i] != 5:
num_list.append(x[i])
i = i + 1
return num_list
print(sublist([1, 2, 5, 7])) #works fine
print(sublist([1, 2, 7, 9])) #list index out of range error

As others have pointed out, it is best to use a for loop when you already know the number loops you need (e.g.: len(x) in your case).
If you still really want to use a while loop instead, you will need to check every loop to see if you have checked every item in the old list, and exit the loop if you do. Here's what your code could look like if you're using a while loop:
def sublist(x):
i = 0
num_list = []
original_length = len(x)
while i < original_length and x[i] != 5:
num_list.append(x[i])
i = i + 1
return num_list
print(sublist([1, 2, 5, 7])) #works fine
print(sublist([1, 2, 7, 9])) #now also works fine
EDIT: I originally had the check for i < original_length inside the loop, I've changed it to be inside the while condition. BUT BE CAREFUL because the check MUST come before x[i] != 5. What I mean is that using this will work:
while i < original_length and x[i] != 5:
But this will not:
while x[i] != 5 and i < original_length:

This should do the trick. If n (5 in your example) is in the list x it will take the list until that point. Else, it will take the whole list. This isn't the most pythonic option, though, probably someone knows a better way.
def sublist(x, n):
num_list=[]
if n in x:
for i in x:
while i!=n:
num_list.append(i)
else:
num_list=x
return num_list

Your loop will not stop if there is no 5 in the list, so the index i will be equal to the length of x (length of x is out of range) in the iteration when the error happens.
When looping with python, it's better to use for in loops:
def sublist(x):
num_list = []
for n in x:
if n != 5:
num_list.append(n)
else:
break
return num_list
print(sublist([1, 2, 5, 7])) # [1, 2]
print(sublist([1, 2, 7, 9])) # [1, 2, 7, 9]

Try this
def sublist(x):
x.sort()
limit_num = 5
if limit_num not in x:return x
return x[:x.index(limit_num)]
print(sublist([1, 2, 5, 7]))
print(sublist([1, 2, 7, 9]))
Result:[1, 2]
[1, 2, 7, 9]

Related

Python, looping and get the first max value

Let's say I have this list containing these numbers: 1, 4, 8, 3, 5, 9, 2
I want to loop through this and get 8 because it's the first max value I encounter when loop through the list
I'm thinking of loop through each [i], and + the if statement:
if [i] < [i+1] and [i+1] > [i+2]:
print(i)
Although this print 8 which is what I want, it also prints 9 because it satisfied the condition. Is there a way to make it stop after printing 8?!
Below is the code you can try though to get the first max value between i and i+1 index through the list
nums = [1, 4, 8, 3, 5, 9, 2]
for i in range(len(nums)-1):
if nums[i] > nums[i+1]:
print(nums[i])
break
You can use the break statement to break out of your loop. You can check out the use of break, continue and pass statements in Python here,
https://www.geeksforgeeks.org/break-continue-and-pass-in-python/
The break statement is what is applicable to you here.
So, your code would look something like this,
for i in range(len(nums)-1):
if [i] < [i+1]:
print(i)
break
By "first max", I'm assuming you mean the point where the immediate next element is lower?
Thats what I understood also going by your example -
Want 8 as the output out of [1, 4, 8, 3, 5, 9, 2]
If that is what you're looking for, you can try this -
a = [1, 4, 8, 3, 5, 9, 2]
for idx, val in enumerate(a):
if val > a[idx+1]:
# print(f"{idx} : {val}")
print(val)
break
I'm checking for the 1st occurrence where an element is higher than its very next element and breaking out of the loop at that point.

How do I mutate every third element in a list? [duplicate]

This question already has answers here:
Edit the value of every Nth item in a list
(7 answers)
how to replace every n-th value of an array in python most efficiently?
(3 answers)
Closed 3 years ago.
How do I check every third element in a list?
ie. Given the list below, I want to mutate every third item and leave the rest of the list as is.
L = [1,2,3,4,5,6,7,8,9]
Basically, if the third element in the list (in this case 3,6,9) is odd, then subtract one to make it even. If it is even, let it remain the same.
I know that if I do L[2::3] I get a new list of every third element, but I simply want to loop through every third item and check.
What would the for loop look like? I need some help.
Thanks and appreciate all help!
Don't overthink this. This is a case where list comprehensions, functions, and so on are all overkill. You need to iterate over a list, and modify it in-place, right? So you're not modifying the structure you're iterating over. The iteration target is actually the indices of the list.
xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(xs)
for idx in range(2, len(xs), 3):
if xs[idx] % 2 == 1:
xs[idx] -= 1
print(xs)
This is the equivalent of a for(i = 2; i < len(xs); i++) loop in many lower-level languages.
Compared to the list comprehension solution proposed in another answer, the for loop is marginally faster:
from timeit import timeit
def in_place(xs):
for idx in range(2, len(xs), 3):
if xs[idx] % 2 == 1:
xs[idx] -= 1
return xs
def list_comp(xs):
xs[2::3] = [i - 1 if (i % 3 == 0 and i % 2 != 0) else i for i in xs[2::3]]
return xs
# This answer is an improvement, as it eliminates one modulus
def list_comp2(xs):
xs[2::3] = [x - 1 if x % 2 else x for x in xs[2::3]]
return xs
context = {"globals": globals(), "setup": "xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]"}
print(f"For loop: {timeit('in_place(xs)', **context)}")
print(f"List comprehension: {timeit('list_comp(xs)', **context)}")
print(f"List comprehension 2: {timeit('list_comp2(xs)', **context)}")
On my machine, this produces:
For loop: 0.608657514
List comprehension: 0.7510721879999999
List comprehension 2: 0.641639047
You can just do a list comprehension using slicing to modify selected values based on condition:
L[2::3] = [x - 1 if x % 2 else x for x in L[2::3]]
Example:
L = [1,2,3,4,5,6,7,8,9]
L[2::3] = [x - 1 if x % 2 else x for x in L[2::3]]
# [1, 2, 2, 4, 5, 6, 7, 8, 8]
You can assign to slices and in this example then use a list comprehension to do the mutation.
L = [1,2,3,4,5,6,7,8,9]
L[2::3] = [ x if x % 2 == 0 else x-1 for x in L[2::3] ]
print(L)
Though despite the syntactic beauty, a simple for loop reads better for your future self and colleagues =D
Define the function you want to apply to the elements an iterate using a list comprehension
def do(number):
if number%2 == 1:
return number -1
else:
return number
[do(y) if i%3 == 0 else y for i,y in enumerate(L)]
a different solution not using enumerate explicitly could be:
for i in range(len(L)):
if i%3 == 0:
L[i] = do(L[i])
You also might want to check this about inlace operations
List comprehension is the best option:
L = [1,2,3,4,5,6,7,8,9]
L[2::3] = [i-1 if (i%3==0 and i%2!= 0) else i for i in L[2::3]
print(L)
Output:
[1, 2, 2, 4, 5, 6, 7, 8, 8]
Explanation:
Basically you look at each third element in L and see if it is divisible by 3 (remainder is 0) AND not even (mod2 is not 0) and if so subtract 1 from it, otherwise keep it as is. Replace result in the original list.

Appending a list through if condition in python

This could be a very basic question, but I realized I am not understanding something.
When appending new things in for loop, how can I raise conditions and still append the item?
For instance:
alist = [0,1,2,3,4,5]
new = []
for n in alist:
if n == 5:
continue
else:
new.append(n+1)
print(new)
Gets me
[1, 2, 3, 4, 5]
How do I get
[1, 2, 3, 4, 5, 5] # 4 is incremented, 5 is added 'as is'
Essentially, I want to tell python to not go through n+1 when n==5.
Would this be the only solution? append n==5 separately in a list and then sum new and the separate list?
Why don't you just append the 5 instead of using continue, is there any other condition?
for n in alist:
if n == 5:
new.append(n)
else:
new.append(n+1)
You can use the fact the a boolean True is 1 while a False is 0 combined with a list comprehension like:
Code:
[x + int(i != 5) for i, x in enumerate(alist)]
Test Code:
alist = [0, 1, 2, 3, 4, 5]
new = [x + int(i != 5) for i, x in enumerate(alist)]
print(new)
Result:
[1, 2, 3, 4, 5, 5]
Seems like you didn't get the point of 'continue'. The Python keyword 'continue', means you do nothing in that if condition, so basically you tell the program "do nothing" when n == 5 and if n is not 5, you do some operation. That's why you got your original result. Hope it may help.

Recursive method to find the minimum number in a list of numbers

Given this sample list:
[5, 3, 9, 10, 8, 2, 7]
How to find the minimum number using recursion? The answer is 2.
I found this in a question paper while I was doing recursion exercises. I can't figure out a way to solve this. To find it, do I have to sort the list first and then there's nothing to do recursively. Can any one show me a path?
This is a recursive implementation of min:
l=[5, 3, 9, 10, 8, 2, 7]
def find_min(l,current_minimum = None):
if not l:
return current_minimum
candidate=l.pop()
if current_minimum==None or candidate<current_minimum:
return find_min(l,candidate)
return find_min(l,current_minimum)
print find_min(l)
>>>
2
Take into account that this should not be used in real programs and should be treated as an exercise. The performance will be worse than the built-in minby several orders of magnitude.
>>> import random
>>> arr=[random.randint(0,8) for r in xrange(10)]
>>> arr
[8, 2, 5, 1, 2, 4, 0, 3, 1, 1]
>>> def func(arr):
if len(arr) == 1:
return arr[0]
else:
return min(arr[0],func(arr[1:]))
>>> f(arr)
0
NB the recursion isn't really needed here.
This answer uses an accumulator to store the min value throughout the recursions.
list = [5, 3, 9, 10, 8, 2, 7]
def min_list(list, min=None):
if len(list) < 1:
return min
return min_list(list[1:], list[0] if min is None or list[0] < min else min)
print(min_list(list))
Thats also working, but only for lists with a length that is a power of two. For other lengths you just have to tweak the split into smaller arrays. The approach is taken from merge sort.
def findCloseToZero(l):
if len(l) == 1:
return l[0]
else:
first = findCloseToZero(l[0:int(len(l)/2)])
sec = findCloseToZero(l[int(len(l)/2):])
return first if abs(first) < abs(sec) else sec
def find_smallest_elem(lst):
k=1
while k != len(lst):
if lst[0] > lst[k]:
return(find_smallest_elem(lst[k:]))
else:
k +=1
return(lst[0])
This seems to works fine
this is my answer using recursion and a line of code
def min_value(n_list):
return n_list[0] if len(n_list) == 1 else min(n_list[0], min_value(n_list[1:]))

Get previous value based in specific item

I am trying a script where we use a list item and then I will get the previous item.
lst = [1, 3, 4, 6, 8, 10]
next_item = next((x for x in lst if x > 6), None)
print next_item #8
This code works correctly for the item after 6. However i need the item before 6.
I am looking in docs for a prev method but i can't find anything related.
Any idea about that?
Assuming by "before" you mean "previous in lst", it is not necessary to use such a complicated way. This
lst[lst.index(6) - 1]
will give
4
while this
lst[lst.index(6) + 1]
will give
8
Of course you should check for index out of bound errors.
You can loop over the reversed list with a reverse condition :
>>> next((i for i in lst[::-1] if i<6),None)
4
If you are looking for the first number larger than 6 in lst, this is one way to do it:
lst = [1, 3, 4, 6, 8, 10]
lst2 =[]
for num in lst:
if num > 6:
lst2.append(num)
lst2.sort()
print lst2[0]
The output is 8

Categories