Reputation: 167
How to better implement such thing:
Loop over the numbers. Each number in diapason and belong to one part of associative array.
E.g.
di = {}
di[ xrange(0,10) ] = "A"
di[ xrange(11,20) ] = "B"
di[ xrange(21,30) ] = "C"
direction = di[ 24 ]
# direction should be "C"
direction = di[ 6 ]
# direction should be "A"
Edit: Populate whole di
with discreet numbers is not my way, because I'm talking about IP addresses, really big data, such as netmasks 255.255.255.255. Such di
will overflow my RAM.
Upvotes: 0
Views: 867
Reputation: 39406
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
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.
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)
Upvotes: 1
Reputation: 10894
If you don't want to populate a dictionary just use a function,
from __future__ import division
import string
def return_ltr(i):
n = i//10
return string.ascii_uppercase[n]
Otherwise, if you want to populate a dictionary do the following. Your current code is generating xrange objects
and using them as keys. However, that is not the same as using the numbers for keys. Try the following,
import string
d = {}
i = 0
for ltr in string.ascii_uppercase:
for j in xrange(10):
d[i] = ltr
i += 1
If you only want a subset of the letters you can slice string.ascii_uppercase
. For example using string.ascii_uppercase[0:3]
will just give you A
, B
, and C
.
Upvotes: 0
Reputation: 77912
You could use tuples instead of lists to solve the TypeError
(which your code doesn't raise FWIW) but the lookup still wouldn't work. The simplest thing that could possibly work would be to populate you dict with every discrete key for a given value, ie:
map = (
(0, 10, "A"),
(11, 20, "B"),
# etc
)
d = {}
for start, stop, val in map:
for k in xrange(start, stop):
d[k] = val
Upvotes: 0
Reputation: 880329
di = dict.fromkeys(xrange(0, 10), 'A')
di.update(dict.fromkeys(xrange(10, 20), 'B'))
di.update(dict.fromkeys(xrange(20, 30), 'C'))
print(di)
yields
{0: 'A', 1: 'A', 2: 'A', 3: 'A', 4: 'A', 5: 'A', 6: 'A', 7: 'A', 8: 'A', 9: 'A', 10: 'B', 11: 'B', 12: 'B', 13: 'B', 14: 'B', 15: 'B', 16: 'B', 17: 'B', 18: 'B', 19: 'B', 20: 'C', 21: 'C', 22: 'C', 23: 'C', 24: 'C', 25: 'C', 26: 'C', 27: 'C', 28: 'C', 29: 'C'}
Note that xrange(N, M)
generates ints starting at N
and ending at M-1
(inclusive). So if you want to include 10, 20 in the keys, then the xranges should be xrange(10, 20)
and xrange(20, 30)
.
Upvotes: 0