konstantin
konstantin

Reputation: 893

Find index inside a list of tuples

I have a list of tuples seg = [(874, 893), (964, 985), (1012, 1031)] and an index. I want to check if the index is inside the range of those tuples, for example, 876 is while 870 is not.

My code to do so is the following:

if [x for (x, y) in seg if x <= index <= y]:
   print ("index inside the segment")

However, I also want to return if the index is in the first second ... segment of the list seg.

For example, for the index = 876 to return 1 and for the index = 1015 to return 3.

How can I do so?

Upvotes: 1

Views: 2085

Answers (3)

Joe Iddon
Joe Iddon

Reputation: 20424

You can use enumerate + next with generator expression:

>>> seg = [(874, 893), (964, 985), (1012, 1031)]
>>> index = 876
>>> next((i for i, (s,f) in enumerate(seg) if s <= index <= f), None)
0

Or, if you want to iterate over:

>>> for i in (i for i, (s,f) in enumerate(seg) if s <= index <= f):
...     print("in segment:", i)
... 
in segment: 0

thanks @jpp for the hint about the default option of the next function. (It can be used in cases where the given index is not in any of the ranges represented by the tuples)

Upvotes: 8

Gareth Latty
Gareth Latty

Reputation: 89017

As others have pointed out, you can use enumerate() to get the indexes. I'd also argue that if you are treating the tuples as ranges, you should make them ranges. This then makes the check to see if the value is inside the range very intuitive: value in range.

import itertools

seg = [(874, 893), (964, 985), (1012, 1031)]
ranges = list(itertools.starmap(range, seg))

def test(value):
  for i, valueRange in enumerate(ranges):
    if value in valueRange:
      return i # + 1 if you want to index from 1 as indicated.
  # You could add some special case handling here if there is no match, like:
  # throw NoSuchRangeException("The given value was not inside any of the ranges.")

print(test(876)) # 0
print(test(1015)) # 1

Obviously using ranges has some cost (if you are in Python 2.x, this is comparatively huge because it will make actual lists of all the values, and unfortunately xrange() return objects without __contains__() implemented). If you are doing this kind of thing in lots of places, it's much nicer, however.

You may be able to just replace your tuple construction with range construction, depending on the situation, rather than doing the starmap.

Upvotes: 3

Michael Robellard
Michael Robellard

Reputation: 2358

Assuming the following:

List is ordered

First number is less than second number

no overlapping

index=870
seg = [(874, 893), (964, 985), (1012, 1031)]
for i, t in enumerate(seg):
    if index >= t[0] and index <= t[1]:
        print i

Upvotes: 1

Related Questions