alancalvitti
alancalvitti

Reputation: 476

Test if an integer is an index value of a slice

Is there a Boolean function to test whether an integer is an index value contained in a slice object without unpacking the start, stop, and step parameters?

3 in slice(1, 6, 2)

raises an error as slices are not iterable.

The predicate should work for arbitrary None, start, stop, step parameters. The logic is straightforward but hoping there's a built in or package.

Upvotes: 8

Views: 724

Answers (2)

Mad Physicist
Mad Physicist

Reputation: 114440

The logic is not as straightforward as you think, since it doesn't make sense to do this for a None stop or start (depending on the sign of step), since you need to specify a length.

Essentially, what you are asking for is containment in a range object, which holds the same information as a slice, but is a valid sequence, and supports fast containment checking. slice has a method called indices to help with the transformation, if you provide the length of the sequence you are interested in slicing:

def in_slice(n, s, length):
    return n in range(*s.indices(length))

Upvotes: 11

Alain T.
Alain T.

Reputation: 42133

If you can select an appropriately sized range, you can apply the slice to it and determine if the index will be in the range.

for positive/None boundaries, this is merely a matter of having a big enough range to contain the index itself:

i in range(i+1)[slice(start,stop,step)]

3 in range(7)[slice(1,6,2)] --> True

In order to support negative boundaries (in particular a negative start), you'll need to ensure that index offset from start is a multiple of the step and that the size is small enough to include the index. This is under the assumption that there exists a range size that will contain the index. Otherwise you'll have to supply a range size yourself:

def inSlice(i,s, size = None):
    if size is None:
        step  = s.step  or 1
        start = s.start or 0
        size  = size or i+1
        if step < 0 or start <= 0:
           size = (i-min(0,start))//step*step - int(s.stop is None)
    return  i in range(size)[s]

This will provide the following results:

inSlice( 3 , slice(1, 6, 2) )           -->  True ( size = 4 )
inSlice( 37000216 , slice(10, 100000000, 3) )   -->  True ( size = 37000217 )
inSlice( 17 , slice(-100, 10, -3) )     -->  True ( size = 117 )
inSlice( 17 , slice(-100, -10, 3) )     -->  True ( size = 117 )
inSlice( 97 , slice(100, None, -3) )    -->  True ( size = 98 )
inSlice( 107 , slice(None, 100, -3) )   -->  True ( size = 108 )
inSlice( 97 , slice(-10, None, -3) )    -->  True ( size = 107 )

Upvotes: 0

Related Questions