0

So i am new to programming, and i am having trouble with index out of range errors. Quick example:

I have a list, lst = (5,7,8,9,10).

I want to remove every even number, and every number to the right of an even number.

I would approach this problem by getting the index of every even number, 'i' , and removing lst[i] and lst [i+1]. That will not work when the last number is even because there is no lst [i+1] after the last element in the list.

I have run into this issue on several basic problems i have been working on. My approach to solving this is probably wrong, so i would like to know:

  1. How can i/Can i solve the problem this way, whether it is efficient or not?
  2. What would be the most efficient way to solve this problem?
petezurich
  • 9,280
  • 9
  • 43
  • 57
jra
  • 19
  • 1
  • 5

3 Answers3

1

Welcome to the club! Programming is a lot of fun and something you can always improve upon with incremental progress. I'm going to try to be exhaustive with my answer.

With lists (also known as arrays) remember that a list and its indexes are zero-based. What this means is that an array's indexes start at the number 0 (not number 1 like you would do in normal counting).

arr = [5, 7, 8, 9, 10]

# If you want to access the first element of the array
# then you would use the 0 index. If you want the Second
# element you use index 1.

print(arr[0]) # prints 5 or the 1st element
print(arr[1]) # prints 7 or the 2nd element

I would not use your stand looping technique like for or while in this case because you are removing elements are you are going for the array. If you delete the item as you are looping you are changing the length of the array.

Instead, you could create a new array from looping and only adding or appending odd values to this new array.

arr = [5, 7, 8, 9, 10]
new_arr = []

for idx, val in enumerate(arr):
    if idx % 2 == 1:
        new_arr.append(val)
return new_arr # yields [7,9] or this process creates a new array of odd elements

In addition, remember when you are using [i+1] while you are indexing through loop in makes sense to stop the loop an element early to avoid an out of index range error.

Do this (no error)

for idx in range(len(arr)-1):
    # pseudocode
    print(arr[i] + arr[i+1])

instead of this (out of index error). The reason being is that on the last element if you try to add 1 to last index and then access a value that does not exist then an error will be returned:

for idx in range(len(arr)):
    # pseudocode
    print(arr[i] + arr[i+1])
arr = [5, 7, 8, 9, 10]
# if you try to access arr[5]
# you will get an error because the index 
# and element do not exist
# the last element of arr is arr[4] or arr[-1]
arr[5] # yields an out of index error

There are many Pythonic (almost like a colloqial phrase specific to python) ways to accomplish your goal that are more efficient below.

You can use slicing, spacing and the del (delete statment) to remove even number elements

>>> arr = [5, 7, 8, 9, 10]
>>> del arr[::2] # delete even numbers # if you wanted to delete odd numbers del arr[1::2]
>>> arr
[7, 9]

Or a list comprehension to create a new list while looping through some conditional to filter the even numbers out:

new_arr = [elem for idx, elem in enumerate(arr) if idx % 2 == 0]

The % operator is used to see if there is a remainder from division. So if idx is 10. Then 10 % 2 == 0 is true because 2 is able to divide into 10 five times and the remainder is 0. Therefore, the element is even. If you were checking for odd the condition would be:

idx % 2 == 1

You can find further explanation of these Python methods from this great Stack Overflow post here

Kevin Carr
  • 56
  • 3
1

One issue you may run into is your list indexes shifting on you during removal. One way around this is to sort the indexes to be removed in descending order and remove them first.

Here is an example of how you could accomplish what you are looking for:

myList = [5, 7, 8, 9, 10]

# use list comprehension to get indexes of even numbers into a list.
# num % 2 uses the modulus operator to find numbers divisible by 2
# with a remainder of 0.
even_number_indexes = [idx for idx, num in enumerate(myList) if num % 2 == 0]
# even_number_indexes: [2, 4]

# sort our new list descending
even_number_indexes.reverse()
# even_number_indexes: [4, 2]

# iterate over even_number_indexes and delete index and index + 1
# from myList by specifying a range [index:index + 2]
for index in even_number_indexes:
    del myList[index:index + 2]


print(myList)

output: [5, 7]

Alec Thomas
  • 176
  • 1
  • 2
  • 12
0

You can check if i+1 is greater than (Edit: or equal to) the length of the list, and if it is, not execute the code.

You can also handle this in a try/except block.

As to the efficiency of this method of solving, seems fine to me. One gotcha in this approach is that people try to iterate over the list while modifying it, which can lead to unknown errors. If you're using the remove() function, you probably want to do it with a copy of the list.

Audemed
  • 196
  • 4