I have a dictionary that maps 3tuple to 3tuple where key-tuples have some element in common
dict= { (a,b,c):(1,2,3),
(a,b,d):tuple1,
(a,e,b):
You could reconstruct your dictionary into a triply nested dict.
dict= { ("foo", 4 , "q"): 9,
("foo", 4 , "r"): 8,
("foo", 8 , "s"): 7,
("bar", 15, "t"): 6,
("bar", 16, "u"): 5,
("baz", 23, "v"): 4
}
d = {}
for (a,b,c), value in dict.iteritems():
if a not in d:
d[a] = {}
if b not in d[a]:
d[a][b] = {}
d[a][b][c] = value
Here, d
is equivalent to:
d = {
"foo": {
4:{
"q": 9,
"r": 8
},
8:{
"s": 7
}
},
"bar":{
15:{
"t": 6
}
16:{
"u": 5
}
},
"baz":{
23{
"v": 4
}
}
}
Now you can easily iterate through the possible third keys, given the first and second.
#find all keys whose first two elements are "foo" and 4
a = "foo"
b = 4
for c in d[a][b].iterkeys():
print c
Result:
q
r
This only works for matching the third key. For instance, you wouldn't be able to find all second keys, given the third and the first.
There might be other ways, but assuming you just need to do a single search (in other words there might be ways to build better data structures for repeated searching): (Note that this handles arbitrary lengthed tuple's with the '*' in multiple possible locations)
def match(tup,target):
if len(tup) != len(target):
return False
for i in xrange(len(tup)):
if target[i] != "*" and tup[i] != target[i]:
return False
return True
def get_tuples(mydict,target):
keys = filter(lambda x: match(x,target),mydict.keys())
return [mydict[key] for key in keys]
#example:
dict= { (1,3,5):(1,2,3),
(1,3,6):(1,5,7),
(1,2,5):(1,4,5),
}
print get_tuples(dict,(1,3,'*'))
.
@AshwiniChaudhary's solution can be trivially adapted for an object-oriented solution. You can subclass dict
and add a method:
class tup_dict(dict):
def getitems_fromtup(self, key):
for k, v in self.items():
if all(k1 == k2 or k2 is None for k1, k2 in zip(k, key)):
yield v
d = tup_dict({("foo", 4 , "q"): 9,
("foo", 4 , "r"): 8,
("foo", 8 , "s"): 7,
("bar", 15, "t"): 6,
("bar", 16, "u"): 5,
("baz", 23, "v"): 4})
res = list(d.getitems_fromtup(("foo", 4, None))) # [9, 8]
Lets say if you're passing None
for the missing keys then you can use all
and zip
:
>>> from itertools import permutations
>>> import random
#create a sample dict
>>> dic = {k:random.randint(1, 1000) for k in permutations('abcde', 3)}
def partial_match(key, d):
for k, v in d.iteritems():
if all(k1 == k2 or k2 is None for k1, k2 in zip(k, key)):
yield v
...
>>> list(partial_match(('a', 'b', None), dic))
[541, 470, 734]
>>> list(partial_match(('a', None, 'b'), dic))
[460, 966, 45]
#Answer check
>>> [dic[('a', 'b', x)] for x in 'cde']
[541, 734, 470]
>>> [dic[('a', x, 'b')] for x in 'cde']
[966, 460, 45]