1

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?

Drise
  • 4,310
  • 5
  • 41
  • 66
konstantin
  • 853
  • 4
  • 16
  • 50
  • Are you looking for something like `print([i+1 for (i, (x, y)) in enumerate(seg) if x <= index <= y])`? – pault Mar 19 '18 at 17:50

3 Answers3

8

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)

Joe Iddon
  • 20,101
  • 7
  • 33
  • 54
3

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.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • Yes, I was also thinking about making the tuples ranges. I didn't know how efficiency would be affected though. – Christian Dean Mar 19 '18 at 17:57
  • @ChristianDean It depends on how often you are doing it, and how you use the values. In most cases the cost should be negligible - ranges are just very small structures around the two values (in Python 3.x). – Gareth Latty Mar 19 '18 at 17:58
1

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
Michael Robellard
  • 2,268
  • 15
  • 25