Home > Software design >  How to move items with even index to the end of the list
How to move items with even index to the end of the list

Time:05-18

lst = ['apple', 'orange', 'kiwi', 'ananas',
       'tea', 'coffee', 'milk', 'love', 'peace']

for i in range(len(lst)):
    if (i   1) % 2 == 0:
        lst.append(lst[i])
        lst.pop(i)

Basically here I want the items with even index to be added at the end of this list it works for the second item but still doesn’t for the rest of them

CodePudding user response:

You can use Python's wider-step ranges:

lst = lst[1::2]   lst[0::2]

The right hand side of the plus says "grab every 2nd element starting from the first" and the left hand side says "grab every 2nd element starting from the second". This basically reconstructs the list with the odd elements first and the even elements last.

It even avoids expensive pops that make your reference algorithm O(n^2)

CodePudding user response:

The problem with your approach is that the elements shift after you moved the first element. So when you are at the next element with "even" index, the element that's there was originally at an odd index. Thus, after you shift the first element, you can just directly continue with the element at the next index, which previously was two indices away, then again the next one, and so on, for half the indices in the list.

Here's an example, using a list of numbers so it's easier to see what happens. If you want odd indices instead, use range(1, len(lst)//2 1).

lst = list(range(10))
for i in range(len(lst)//2):
    lst.append(lst.pop(i))
# [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]

However, even if this works, modifying a list while iterating it is generally a very bad idea leading to many headaches. Also, that repeated pop(i) makes the whole operation O(n²).

Instead, it would be much faster and saner to just combine two slices of the list:

lst = list(range(10))
lst = lst[1::2]   lst[0::2]
# [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]

(If you need to change the list "in-place", e.g. because of other references pointing to that list, you can replace the content of the list using an assignment to a slice: lst[:] = .... This would still not be "in-place" in the sense of not using additional memory. But if the list is so big that this is a problem, then the O(n²) running time will probably be a bigger problem anyway.)

CodePudding user response:

A simple way would be to build a new list by using comprehensions:

lst2 = [v for i, v in enumerate(lst) if i%2 == 0]   \
       [v for i, v in enumerate(lst) if i%2 != 0]

But it is possible to change the list in place. The rule is to start from the end of the list in order not to change oddness of indices when an element is removed

last = len(lst) - 1   # when an element is popped, the list loses one element
for i in range(len(lst), 0, -1):
    if (i % 2) == 0:
        val = lst.pop(i - 1)       # remove element with even index
        lst.insert(last, val)      # insert it before last inserted
        last -= 1
  • Related