I would create a custom dict that takes xranges as keys :
class DictRange(dict):
def __getitem__(self, val):
for key in self.keys():
if val in key:
return super(DictRange,self).__getitem__(key)
The drawback is that you have to go through all the keys to find your element. Use like that :
di = DictRange()
di[ xrange(0,10) ] = "A"
di[ xrange(11,20) ] = "B"
di[ xrange(21,30) ] = "C"
print di[24]
# C
print di[6]
# A
See http://repl.it/WAJ
Update
Using bisect and assuming that you can spare some time during initialization to speed up access, you can do something like:
import bisect
class DictRange(dict):
def __setitem__(self, key, val):
super(DictRange,self).__setitem__(key, val)
self.ks = [key[0] for key in self.keys()]
def __getitem__(self, val):
return super(DictRange,self).__getitem__(self.keys()[bisect.bisect(self.ks, val) - 1])
the get will be O(logn)
where n is the number of keys, but the set will become O(n)
. As for the next solution, it relies on the ranges being contiguous.
Other solution
An other solution, which could be faster, depending on your range sizes, would be to use as key the first item of the range, and a bisect to find the proper key:
import bisect
ranges = [0, 11, 21, 31]
di = {}
di[0] = 'A'
di[11] = 'B'
di[21] = 'C'
di[31] = 'unknown' # special value to indicate the end of the range
print di[ranges[bisect.bisect(ranges, 24) - 1]]
# C
print di[ranges[bisect.bisect(ranges, 6) - 1]]
# A
print di[ranges[bisect.bisect(ranges, 31) - 1]]
# Unknown
This will be much faster. bisect
is O(logn)
where n
is the number of ranges, the rest is O(1)
di[range] = value
, how many ranges are there, and how many values? You most likely want some kind of binary tree-based approach (actually list +bisect
) or a graph model of some kind.