Deleting first even number from list with loops: differece between for and while

Hi, I am currently solving a little question on loops from Codecademy projects, question is as follows:
make a function for Deleting Starting Even Numbers
Let’s try a tricky challenge involving removing elements from a list. This function will repeatedly remove the first element of a list until it finds an odd number or runs out of elements. It will accept a list of numbers as an input parameter and return the modified list where any even numbers at the beginning of the list are removed.
So, here’s my solution:

def delete_starting_evens(lst):
  for num in lst:
    if lst[0] % 2 == 0:
      lst = lst[1:]
  return lst

here I used for loop, but as per Codecademy solution the code is with while loop as follows:

def delete_starting_evens(lst):
  while (len(lst) > 0 and lst[0] % 2 == 0):
    lst = lst[1:]
  return lst

And some examples to try this function are:

print(delete_starting_evens([4, 8, 10, 11, 12, 15]))
print(delete_starting_evens([4, 8, 10]))

which will give output same as per both codes, for and while:
output:

[11, 12, 15]
[]

But my question here is, what is the difference made by while loop rather than for loop (I know solution with for loop is larger than while loop here, but please tell me difference made)
xxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxx

Second question on the same function :
if I used the “for” loop and make the solution as follows:

def delete_starting_evens(lst):
  for num in lst:
    if lst[0] % 2 == 0:
      del lst[0]
  return lst

here I used the del function to remove the first element in the list which works fine if a list contains even as well as odd numbers, but if it contains all even numbers it should remove every even number from list and return an empty list, but it is not returning empty list as in this example below:

print(delete_starting_evens([4, 8, 10]))

it gives output as:

[10]

rather than creating an empty list.

Please reply with an explanation of both questions. :slight_smile:

What is the difference made by while loop rather than for loop.

The while loop will continue until the condition is met. This can result in infinite loops. You need to be very careful about the conditions in these. However, it allows you to edit the list without confronting errors because the list size is changing.

The for loop will only go through all the items in the list it was given. This can never result in an infinite loop. However, it is generally a bad idea to remove or add items to a list you are looping through. This can and will lead to errors.

Here I used the del function to remove the first element in the list which works fine if a list contains even as well as odd numbers, but if it contains all even numbers it should remove every even number from list and return an empty list, but it is not returning empty list …

You are encountering an issue caused by editing a list being used in a for loop. Don’t do this. The best solution, if you plan on adding or removing from the list, is to use a while loop.

Thanks, @brianmonette for your reply.
I think in short you mean, for loop is used for finite lists, and while loop is used for infinite loops.
But still, as I am a beginner I didn’t get my second question’s answer:

def delete_starting_evens(lst):
  for num in lst:
    if lst[0] % 2 == 0:
      del lst[0]
  return lst

when I used this for the first example which contains odd and even numbers both in a list, it gives correct output but the problem occurs when the list contains only even numbers, suppose [10,12,14] This is the list. after deleting 10 and 12 as first even numbers the list remains as [14]. Here, again lst[0] is 14, then why not it get deleted.

The for loop goes through each element of a list. If the list keeps changing length, this will lead to errors.

While loops don’t care about the list. Change is all you like. They only care about the conditional statement.

The issue you are having is that the list is shrinking as you loop through it. You are editing the list length as you loop through it. The for loop relies on the length of the list to find the location of the current item. When the length is 1 and it looks for the next item, it finds nothing. Thus the last item isn’t deleted.

Do not edit the length of the list you are looping through. Use a while loop.

1 Like

But why it is looking for next item? as the index I want to delete is lst[0] and which will be present always till it got empty.

Let me explain:

  • The loop is on item 0.
  • item 0 is deleted.
  • The loop looks for the next item, item 1.
  • Item 1 does not exist.
  • The loop gives up and dies.
1 Like

Thanks for your efforts to explain me.

is there any way to stop looking the loop for next item after item 0 get deleted? like using break or any other method.

The solution is two-fold.

You can either store the solution in a new array or use a while loop.

def delete_starting_evens(lst):
  solution = []
  for num in lst:
    if lst[0] % 2 == 0:
      solution = lst[1:]
  return solution

def delete_starting_evens(lst):  
  while lst[0] % 2 != 0 and len(lst) > 0:
      lst = lst[1:]
  return lst
1 Like

this code is not giving reqired output, it is only removing first element once.

That’s because the lst[0] only checks the first item of the list for every iteration. What I’m sure they meant was use num in place of lst[0]. That would check to see if num was even or not.

I’m really not getting this,
Please can anyone make a solution for this problem using for loop as well as del function?

For-loop is not ideal in for this problem because it’s creating an iterator and the expression is evaluated once. See documentation for more info.
Hence, I will use the for-loop to search for the first non-even number (if exists), then get hold of the index position to perform slicing delete. In this way, we avoid modifying (delete) the list and getting back into the for-loop. Here’s my attempt and I find it looks urgly :woozy_face::

def delete_starting_evens(lst):
    for i in range(len(lst)):
        if lst[i] % 2 != 0:
            del lst[:i]
            break
    else:
        del lst[:]

    return lst

print(delete_starting_evens([4, 8, 10, 11, 12, 15]))
print(delete_starting_evens([4, 8, 10]))
print(delete_starting_evens([11, 15]))

[11, 12, 15]
[]
[11, 15]
2 Likes

Hey,
Thanks, man, I got it, though code goes complicated using “del” as well as “for” loop.
Thanks, everyone, for helping. @brianmonette @ajax5260537031 @codeneutrino :slight_smile:

1 Like

Your question made me curious so I was playing with it. Making another list makes the for loop possible, though you’re better off using the while loop.

def delete_starting_evens(lst):
    rtnlst = lst
    for i in range(len(lst)):
      if rtnlst[0] % 2 == 0:
        del rtnlst[0]
    
    return rtnlst

print(delete_starting_evens([4, 8, 10, 11, 12, 15]))
print(delete_starting_evens([4, 8, 10]))
print(delete_starting_evens([11, 15]))

[11, 12, 15]
[]
[11, 15]
2 Likes

hey @bavarcarus, you solved it. Now I got it, we should assign lst to another variable (here rtnlst) and use that as a final resultant list.
Can you explain to me, when we don’t assign our list to another then it doesn’t work like our way, but when we assign it to another variable it starts working. Why does this happen? Just curious about the functioning of this syntax.
Thanks :smiley:

Now that I look at it again, it returns fine even without the list swap. I was just pointing out that if iterating over a changing list in a for loop is error prone, you could create another list for modification.

The main issue with the first code, according to this visualization tool, is that for i in lst caused python to ignore the deletion events when interating. http://www.pythontutor.com/visualize_for_i_in_lst So at the i=lst[0] iteration it deletes lst[0]. The next iteration, i=lst[1], will look at lst[0] of lst=[8, 10]. This passes the modulo logic to delete lst[0]=8. The next iteration would be i=lst[2] of lst=[10], which is null so it does not execute the modulo logic on lst and returns lst=[10].

When you change to for i in range(len(lst)):, I suspect it creates a new range object/variable at the beginning of the for loop, but this is just a guess on my part. I’m just learning this stuff. The visualize tool shows the code iterating from 0 to 2 even though on the last iteration, lst=[10] and len(lst)=1. http://www.pythontutor.com/visualize_for_i_in_range(len(lst))

If lst is the first 10 billion numbers in pi, a while loop would look at and delete 3 and 1. When it got to 4 it would look at it and stop, returning the rest of the list. The for loop that I put up there looks at every number in the list even after lst[0] is even. The one with the break farther up will stop but it probably still consumes some resources setting up range(len(lst)).