Slice a list based on an index and items behind it in Python
Arithmetic with angles
Your goal isn't to slice, concatenate or reverse lists. Your goal is to do basic arithmetic with degrees and keep the results between 0
and 359
. For this, you really should use the modulo operator %
:
>>> 90 % 360
90
>>> 390 % 360
30
>>> -60 % 360
300
>>> 360 % 360
0
Back to the question
If you only want to use this slicing for degrees with a constant increment, you could generate the desired list directly:
>>> STEP = 15
>>> list(range(0, 360, STEP))
[0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345]
>>> def previous_degrees(start, n, step=STEP):
... return [(start - i * step) % 360 for i in range(n + 1)]
...
>>> previous_degrees(90, 12)
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]
>>> previous_degrees(90, 12, 30)
[90, 60, 30, 0, 330, 300, 270, 240, 210, 180, 150, 120, 90]
>>> previous_degrees(90, 6, 45)
[90, 45, 0, 315, 270, 225, 180]
Your real question
You wrote in a comment:
This array of degrees is designed to work with a smooth rotation system that I'm trying to create in pygame. Normally I would just find the difference between the current direction and the target direction and increment from there, but since the rotation rolls over at zero I have to hardcode the values to make sure that it will always go the shortest route possible.
From two angles, you need to determine if you should turn clockwise or anticlockwise. You can use modulo again to make sure that the rotation will be between -180° and 179°:
def shortest_rotation(start_angle, end_angle):
return (end_angle - start_angle + 180) % 360 - 180
Here's an example:
>>> shortest_rotation(0, 90)
90
>>> shortest_rotation(90, 0)
-90
>>> shortest_rotation(90, 90)
0
>>> shortest_rotation(90, 330)
-120
>>> shortest_rotation(0, 180)
-180
>>> shortest_rotation(0, 181)
-179
>>> shortest_rotation(0, 179)
179
>>> shortest_rotation(10, 350)
-20
You can now create a list of angles, turning in the shortest direction:
def rotation_steps(start_angle, end_angle, n):
increment = shortest_rotation(start_angle, end_angle) / n
return [(start_angle + i * increment) % 360 for i in range(n + 1)]
As an example:
>>> rotation_steps(90, 270, 12)
[90.0, 75.0, 60.0, 45.0, 30.0, 15.0, 0.0, 345.0, 330.0, 315.0, 300.0, 285.0, 270.0]
>>> rotation_steps(10, 350, 2)
[10.0, 0.0, 350.0]
The list uses float in order to avoid missing the end_angle
if increment
isn't an integer.
Something like this might be more direct:
index = DEGREES.index(90)
print([DEGREES[i] for i in range(index, index-13, -1)])
For these cases, a NumPy function that comes in handy is np.roll
, which, as its name specifies, rolls the elements in the array, and as also as mentioned in the documentation:
Elements that roll beyond the last position are re-introduced at the first
Which is exactly what we need in order to roll at the back the first items in the list up to the index where 90
appears.
So one approach could be to use the index where 90
appears using the index
list method, and shift the array up to -k
positions, k
being the given index. Then we can just slice the list and take its last n
elements reversed:
import numpy as np
l = [
0, 15, 30, 45, 60,
75, 90, 105, 120,
135, 150, 165, 180,
195, 210, 225, 240,
255, 270, 285, 300,
315, 330, 345,
]
def roll_n_reversed(l, i, n):
return np.roll(l, -l.index(i)-1)[:-(n+1):-1]
roll_n_reversed(l, 90, 13)
Which yields the expected output:
array([ 90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270])
Or you could use a deque
:
from collections import deque
from itertools import islice
dq = deque(reversed((0, 15, 30, 45, 60,
75, 90, 105, 120,
135, 150, 165, 180,
195, 210, 225, 240,
255, 270, 285, 300,
315, 330, 345)))
index = dq.index(90)
dq.rotate(-index)
res = list(islice(dq, 13))
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]
You could use that as a function:
def f(i):
dq.rotate(-dq.index(i))
return list(islice(dq, 13))
# f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]