How to Enumerate a Slice Using the Original Indices

How to enumerate a slice using the original indices?

enumerate() returns a sequence of (n, elem) pairs, where the ns are consecutive Ints starting at zero. This makes sense because it is
a protocol extension method for SequenceType, and an arbitrary sequence
does not necessarily have index associated with the elements.

You would get your expected result with

let powersSlice = slice.indices.map { pow(slice[$0], Double($0)) }

or

let powersSlice = zip(slice.indices, slice).map { pow($1, Double($0)) }

The latter approach could be generalized to a protocol extension
method for arbitrary collections:

extension CollectionType {
func indexEnumerate() -> AnySequence<(index: Index, element: Generator.Element)> {
return AnySequence(zip(indices, self))
}
}

This returns a sequence of (index, elem) pairs where index
is an index of the collection and elem the corresponding element.
AnySequence is used to "hide" the specific type
Zip2Sequence<RangeGenerator<Self.Index>, Self> returned from zip()
from the caller.

Example:

let powersSliceEnumerate = slice.indexEnumerate().map() { pow($0.element, Double($0.index)) }
print("powersSliceEnumerate == \(powersSliceEnumerate)")
// powersSliceEnumerate == [2.0, 9.0]

Update for Swift 3:

extension Collection {
func indexEnumerate() -> AnySequence<(Indices.Iterator.Element, Iterator.Element)> {
return AnySequence(zip(indices, self))
}
}

Enumerating sliced array using original indices

The items in the slice always have indexes that are exactly startIndex less than in the original array:

slice(0, 3) -> [0, 1, 2] -> [0 - 0, 1 - 0, 2 - 0]
slice(3, 6) -> [0, 1, 2] -> [3 - 3, 4 - 3, 5 - 3]
slice(n, …) -> [0, 1, …] -> [n + 0 - n, n + 1 - n, n + 2 - n, …]

So, just adding startIndex back should do the trick:

/**
* @template Item
* @param {Item[]} items
* @param {number} startIndex
* @param {number} [endIndex]
* @returns {Array<{ item: Item; index: number }>}
*/
function slice(items, startIndex, endIndex = items.length) {
return items.slice(startIndex, endIndex).map((item, index) => ({
item,
index: index + startIndex,
}));
}





function slice(items, startIndex, endIndex = items.length) {
return items.slice(startIndex, endIndex).map((item, index) => ({
item,
index: index + startIndex,
}));
}

const letters = [ ...'abcdefghijklmnopqrstuvwxyz' ];

console.log(slice(letters, 0, 3));
console.log(slice(letters, 3, 6));
console.log(slice(letters, 6, 9));

How to obtain the original indexes from a sliced list in python?


>>> def printy(v):
... for x, y in v:
... print('Original index:', x, ', original element:', y)
...
>>> # Original list
... a = [1, 2, 2, 4, 4, 4, 5, 0]
>>> with_original_indexes = tuple(zip(range(len(a)), a)) # or list()
>>> with_original_indexes = tuple(enumerate(a)) # Equivalent to the above
>>> printy(with_original_indexes)
Original index: 0 , original element: 1
Original index: 1 , original element: 2
Original index: 2 , original element: 2
Original index: 3 , original element: 4
Original index: 4 , original element: 4
Original index: 5 , original element: 4
Original index: 6 , original element: 5
Original index: 7 , original element: 0
>>> printy(with_original_indexes[1:5])
Original index: 1 , original element: 2
Original index: 2 , original element: 2
Original index: 3 , original element: 4
Original index: 4 , original element: 4
>>> printy(with_original_indexes[1:5][2:5])
Original index: 3 , original element: 4
Original index: 4 , original element: 4
>>>

Python: slices of enumerate

You can use islice:

from itertools import islice

for i,elm in islice(enumerate(some_list),7,40):
print i,elm

Python: enumerate a slice with negative indices


a = [1,2,3,4,5]

print ([c for c in a[-3:]])

Output:

[3,4,5]

With enumerate:

print ([(i, j)[1] for i, j in enumerate(a[-3:])])

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.

How to enumerate over selected elements from an iterable keeping original indices?

Here my comment as an answer ;)

months = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]

offset = 5
step = 2
for index, element in enumerate(months[offset::step]):

# recalculate original index
index = offset + index*step

# actually repetition of the month is trivial,
# but I put it just to show that the index is right
print(index, element, months[index])

Prints:

5 June June
7 August August
9 October October
11 December December

How to transform a slice to a list of index in python?

You can use the indices method of the slice to generate a tuple that can be passed to range. You need to know the length of your instance though.

For example:

>>> sl = slice(2)  # or wherever you get that from
>>> length = 100
>>> list(range(*sl.indices(length)))
[0, 1]

How to slice a list using indexes in another list

Using itertools.pairwise:

>>> from itertools import pairwise
>>> for i, j in pairwise([0, 888, 1776, 2664, 3551, 4438]):
... print(i, j)
...
0 888
888 1776
1776 2664
2664 3551
3551 4438

What's next? I think you can solve it yourself.

Understanding slicing

The syntax is:

a[start:stop]  # items start through stop-1
a[start:] # items start through the rest of the array
a[:stop] # items from the beginning through stop-1
a[:] # a copy of the whole array

There is also the step value, which can be used with any of the above:

a[start:stop:step] # start through not past stop, by step

The key point to remember is that the :stop value represents the first value that is not in the selected slice. So, the difference between stop and start is the number of elements selected (if step is 1, the default).

The other feature is that start or stop may be a negative number, which means it counts from the end of the array instead of the beginning. So:

a[-1]    # last item in the array
a[-2:] # last two items in the array
a[:-2] # everything except the last two items

Similarly, step may be a negative number:

a[::-1]    # all items in the array, reversed
a[1::-1] # the first two items, reversed
a[:-3:-1] # the last two items, reversed
a[-3::-1] # everything except the last two items, reversed

Python is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[:-2] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen.

Relationship with the slice object

A slice object can represent a slicing operation, i.e.:

a[start:stop:step]

is equivalent to:

a[slice(start, stop, step)]

Slice objects also behave slightly differently depending on the number of arguments, similarly to range(), i.e. both slice(stop) and slice(start, stop[, step]) are supported.
To skip specifying a given argument, one might use None, so that e.g. a[start:] is equivalent to a[slice(start, None)] or a[::-1] is equivalent to a[slice(None, None, -1)].

While the :-based notation is very helpful for simple slicing, the explicit use of slice() objects simplifies the programmatic generation of slicing.



Related Topics



Leave a reply



Submit