Startec
Startec

Reputation: 13246

Python3 Make a list that increments for a certain amount, decrements for a certain amount

if I have the following: [0, 1, 2, 3, 4, 5, 6...] how can I reorder the list (actually make a new copy of the list) and then fill it as such:

[0, 1, 2, 3, 4, 5, 10, 9, 8, 7, 6, 11, 12, 13...]

I.e. Every five iterations, the list starts to decrement, or increment. The reason I want to do this is that I have a list of objects, and I want to fill a new list with the objects in a different order.

One technique I tried is:

copied_icons = [{key:'Object1'}, {key:'Object2'}, {key:'Object3'}...] 
reversed_copied_icons = copied_icons[::-1]
left_to_right = []


for h in range(17):
    left_to_right.append(copied_icons[h])

for j in range(18, 35):
    left_to_right.append(reversed_copied_icons[j])

for k in range(36, 53):
    left_to_right.append(copied_icons[k])

for l in range(54, 71):
    left_to_right.append(reversed_copied_icons[l])

But for some reason this returns the list out of order and duplicates some of the objects. I am wondering if there is a simpler way to alternating incrementing and decrementing while filling my list.

Upvotes: 2

Views: 160

Answers (2)

tobias_k
tobias_k

Reputation: 82949

There are two problems with your approach:

  1. You are reversing the entire list, not just that slice. Let's say the list is [1,2,3,4], and we want to reverse the second half, i.e. get [1,2,4,3]; with your approach, you would take the third and fourth element from the reversed list, [4,3,2,1], and end up with [1,2,2,1]
  2. The to-index in a range is exclusive, thus by using range(17) and then range(18,35) and so forth, you are missing out on the elements at index 17, 35, and 53

You can use a loop for the different parts to be reversed, and then replace that slice of the list with the same slice in reverse order.

lst = list(range(20))
for start in range(5, len(lst), 10):
    lst[start:start+5] = lst[start+4:start-1:-1]

Or this way, as pointed out in comments, which also gets rid of those nasty off-by-one indices:

for start in range(5, len(lst), 10):
    lst[start:start+5] = reversed(lst[start:start+5])

Afterwards, lst is [0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 19, 18, 17, 16, 15].

Or, in case the intervals to be reversed are irregular (as it seems to be in your question):

reverse = [(3, 7), (12,17)]
for start, end in reverse:
    lst[start:end] = reversed(lst[start:end])

Upvotes: 4

Alexander
Alexander

Reputation: 109756

This appears to accomplish your objective:

def foo(lst, n=5):
    """ lst=list to be re-ordered, n=item count before reversal """
    new_list = list()
    direction = 1
    start = 0
    end = start
    while start < len(lst):  
        # process through the list in steps of 'n', 
        # except use min for possible stub at end.
        end = start + min(n, len(lst) - start)  # i.e. start+5
        # If direction is 1, append list to new list. Otherwise, append reversed list.
        new_list[start:end] = lst[start:end][::direction]
        direction *= -1  # Switch directions
        start = end  # Jump to new starting position.
    return new_list

lst = np.arange(20).tolist()
foo(lst,5)
[0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 19, 18, 17, 16, 15]

If the direction *= -1 line where to be removed, the code would simply copy the existing list (lst) in chunks of size 'n', the number of items you'd like before reversing the list.

Just above where the direction is to be changed, the [::direction] will be either [::1] in which case the list will be sorted in regular order or else [::-1] in which case the list will be reversed for the chunk of size n which is being processed. The third argument when slicing a list is the 'step size' argument, so a step of -1 returns a copy of the list in reverse order.

In case there is a stub, i.e. your stub is 2 if your list has 22 elements but your 'n' is in steps of 5, then you need to adjust your step size 'n' so you don't go past the end of your list. The min(n, len(lst) - start) will ensure you don't go past the end of the list. Alternatively, and probably clearer, you could use end = min(start + n, len(lst)).

Upvotes: 1

Related Questions