partial match dictionary key(of tuples) in python

后端 未结 4 1336
长发绾君心
长发绾君心 2020-12-09 13:00

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):         


        
相关标签:
4条回答
  • 2020-12-09 13:07

    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.

    0 讨论(0)
  • 2020-12-09 13:09

    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,'*'))
    

    .

    0 讨论(0)
  • 2020-12-09 13:13

    @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]
    
    0 讨论(0)
  • 2020-12-09 13:15

    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]
    
    0 讨论(0)
提交回复
热议问题