2

I have a list x=[0,1,1,2,3,5,8,3,1] I want to remove list items at odd index only i.e. x[1],x[3],x[5],... etc should be removed. My resulting list should be x=[0,1,3,8,1]

def odd_indexed_list(x):
    ...

x = [0, 1, 1, 2, 3, 5, 8, 3, 1]
#    ^     ^     ^     ^     ^

odd_indexed_list(x)

print(x)
# >> [0, 1, 3, 8, 1]

I want to loop till only one item in the list is remaining. Here's my version of odd_indexed_list()

def odd_indexed_list(x):
    n=len(x)
    while(n>1):
        for j in range(1,n+1,2):
            del x[i]

The error is - IndexError: list assignment index out of range

user9876
  • 10,954
  • 6
  • 44
  • 66
  • Fwiw your algorithm always returns the first element of the list... –  Sep 10 '19 at 19:28
  • @Nico238 0 is not odd – Clade Sep 10 '19 at 19:31
  • 1
    Every time `del x[i]` is executed, the `len(x)` reduces. This results in "out of range" `IndexError`. – Suraj Sep 10 '19 at 19:32
  • Just use a slice: `x[::2]` – juanpa.arrivillaga Sep 10 '19 at 19:33
  • @Clade yes, for a list with one element, it returns the element, for a list with 2 elements it remove element at index 1, for a list with 3 elements it returns a list with 2 elements... At the very end only element at index 0 remains the same. –  Sep 10 '19 at 19:40
  • Possible duplicate of [Remove odd-indexed elements from list in Python](https://stackoverflow.com/questions/28883769/remove-odd-indexed-elements-from-list-in-python) – walnut Sep 10 '19 at 19:40
  • @Nico238 apologies, you're correct – Clade Sep 10 '19 at 20:11

3 Answers3

2

You are trying to access an index that might not exist anymore. i.e.: do not modify a list while iterating over it. Examining your initial control flow, let's look at a simple example:

x = [1, 2, 3]

This has len of 3. However, when you do the following:

del x[1]

x now has the value [1, 3]. The len has changed, and so your final index 2, which was valid at the start of your loop is now invalid. The index is not tied to the state of your list.

With a quick refactor, this can easily be accomplished with a list comprehension:

x = list(range(20))

x = [a for i, a in enumerate(x) if not i%2]

x
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Where enumerate will produce pairs (index, element) of an iterable, and the not i % 2 will ensure that the index is divisible by 2 (not odd). enumerate produces a generator that you can iterate over, and the index in the index, element pairs is directly tied to the original state, allowing you to accurately skip over elements you do not want

C.Nivs
  • 12,353
  • 2
  • 19
  • 44
1

Assuming that you made a mistake by including index 0, this should do it:

x = [0, 1, 1, 2, 3, 5, 8, 3, 1]

while len(x) != 1:
    x = x[::2][1:]

Printing x yields the output:

[8]
Clade
  • 966
  • 1
  • 6
  • 14
0

The process you've described, "remove odd elements from the list and continue until there is only one element remaining", will always give you a list containing one element. So the most efficient way to do it is just to figure out which element that will be, and just take that element from the list. It'll always be the first element. (Because each pass removes x[1] and possibly some other elements x[i] with i > 1, it never removes x[0], so repeating enough times will eventually remove all the elements after x[0] leaving you just x[0] as your only remaining entry). So:

> x = [0, 1, 2, 3, 4]
> print(x[:1])
[0]

However, perhaps you don't need to keep going, you just want the result after one step? In that case:

Python has a way to get every Nth element in a list:

> x = [0, 1, 2, 3, 4]
> print(x[::2])
[0, 2, 4]

Or to get the other half of the list:

> x = [0, 1, 2, 3, 4]
> print(x[1::2])
[1, 3]

You can replace the contents of a list like this:

> x = [0, 1, 2, 3, 4]
> x[:] = [9, 8, 7]
> print(x)
[9, 8, 7]

Putting those together:

> x = [0, 1, 2, 3, 4]
> x[:] = x[::2]
> print(x)
[0, 2, 4]
user9876
  • 10,954
  • 6
  • 44
  • 66