The straightforward answer is to use filter
then max
. Like so
from operator import itemgetter
# straightforward linear search
data = ((1, 33), (2, 24), (3, 42), (4, 2), (8, 12))
filtered = filter(lambda t: 2 <= t[0] <= 4, data)
x, y = max(filtered, key=itemgetter(1))
print(y)
There is however a more efficient way. You could use a binary search to find the start and the end of the range, given that list is ordered by the first tuple element (assumption but it seems that it is from your example). It is probably only needed if your input is quite large, the extra complexity isn't warranted otherwise.
from operator import itemgetter
from bisect import bisect_left, bisect_right
class KeyWrapper:
def __init__(self, iterable, key):
self.it = iterable
self.key = key
def __getitem__(self, i):
return self.key(self.it[i])
def __len__(self):
return len(self.it)
def slicerange(iterable, lo, hi, key=None):
wrapped = KeyWrapper(iterable, key) if key else iterable
start = bisect_left(wrapped, x=lo)
end = bisect_right(wrapped, x=hi, lo=start)
return iterable[start: end]
myrange = slicerange(data, 2, 4, key=itemgetter(0))
x, y = max(myrange, key=itemgetter(1))
print(y)
We are binary searching on an element of the provided data and not using its natural order, so I'm using the KeyWrapper
class from this answer, to allow the binary search with a key function.
You find the start and end, slice it and find the max
with another key function looking at the element at index 1.