In Python, How can I get the next and previous key:value of a particular key in a dictionary?

前端 未结 9 1427
栀梦
栀梦 2021-02-05 05:21

Okay, so this is a little hard to explain, but here goes:

I have a dictionary, which I\'m adding content to. The content is a hashed username (key) with an IP address (v

相关标签:
9条回答
  • 2021-02-05 05:40

    Try:

    pos = 0
    d = {'aaaa': 'a', 'bbbb':'b', 'cccc':'c', 'ffffdd':'d', 'eeee':'e', 'ffff':'f'}
    
    for i in d:
        pos+=1
        if i == 'eeee':
            listForm = list(d.values())
            print(listForm[pos-1])
            print(listForm[pos+1])
    

    As in @AdamKerz's answer enumerate seems pythonic, but if you are a beginner this code might help you understand it in an easy way.

    And I think its faster + smaller compared to sorting followed by building list & then enumerating

    0 讨论(0)
  • 2021-02-05 05:45

    I think this is a nice Pythonic way of resolving your problem using a lambda and list comprehension, although it may not be optimal in execution time:

    import collections
    
    x = collections.OrderedDict([('a','v1'),('b','v2'),('c','v3'),('d','v4')])
    
    previousItem = lambda currentKey, thisOrderedDict : [
        list( thisOrderedDict.items() )[ z - 1 ] if (z != 0) else None
        for z in range( len( thisOrderedDict.items() ) )
        if (list( thisOrderedDict.keys() )[ z ] == currentKey) ][ 0 ]
    
    nextItem = lambda currentKey, thisOrderedDict : [
        list( thisOrderedDict.items() )[ z + 1 ] if (z != (len( thisOrderedDict.items() ) - 1)) else None
        for z in range( len( thisOrderedDict.items() ) )
        if (list( thisOrderedDict.keys() )[ z ] == currentKey) ][ 0 ]
    
    assert previousItem('c', x) == ('b', 'v2')
    assert nextItem('c', x) == ('d', 'v4')
    assert previousItem('a', x) is None
    assert nextItem('d',x) is None
    
    0 讨论(0)
  • 2021-02-05 05:46

    Another way that seems simple and straight forward: this function returns the key which is offset positions away from k

    def get_shifted_key(d:dict, k:str, offset:int) -> str:
        l = list(d.keys())
        if k in l:
            i = l.index(k) + offset
            if 0 <= i < len(l):
                return l[i]
        return None    
    
    0 讨论(0)
  • 2021-02-05 05:47

    Maybe it is an overkill, but you can keep Track of the Keys inserted with a Helper Class and according to that list, you can retrieve the Key for Previous or Next. Just don't forget to check for border conditions, if the objects is already first or last element. This way, you will not need to always resort the ordered list or search for the element.

    from collections import OrderedDict
    
    class Helper(object):
        """Helper Class for Keeping track of Insert Order"""
        def __init__(self, arg):
            super(Helper, self).__init__()
    
        dictContainer = dict()
        ordering = list()
    
        @staticmethod
        def addItem(dictItem):
            for key,value in dictItem.iteritems():
                print key,value
                Helper.ordering.append(key)
                Helper.dictContainer[key] = value
    
        @staticmethod
        def getPrevious(key):
            index = (Helper.ordering.index(key)-1)
            return Helper.dictContainer[Helper.ordering[index]]
    
    
    #Your unordered dictionary
    d = {'aaaa': 'a', 'bbbb':'b', 'cccc':'c', 'ffffdd':'d', 'eeee':'e', 'ffff':'f'}
    
    #Create Order over keys
    ordered = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
    
    #Push your ordered list to your Helper class
    Helper.addItem(ordered)
    
    
    #Get Previous of    
    print Helper.getPrevious('eeee')
    >>> d
    
    0 讨论(0)
  • 2021-02-05 05:50

    You could use a generic function, based on iterators, to get a moving window (taken from this question):

    import itertools
    
    def window(iterable, n=3):
        it = iter(iterable)
        result = tuple(itertools.islice(it, n))
        if len(result) == n:
            yield result
        for element in it:
            result = result[1:] + (element,)
            yield result
    
    l = range(8)
    for i in window(l, 3):
        print i
    

    Using the above function with OrderedDict.items() will give you three (key, value) pairs, in order:

    d = collections.OrderedDict(...)
    
    for p_item, item, n_item in window(d.items()):
        p_key, p_value = p_item
        key, value = item
        # Or, if you don't care about the next value:
        n_key, _ = n_item
    

    Of course using this function the first and last values will never be in the middle position (although this should not be difficult to do with some adaptation).

    I think the biggest advantage is that it does not require table lookups in the previous and next keys, and also that it is generic and works with any iterable.

    0 讨论(0)
  • 2021-02-05 05:54

    Edit: OP now states that they are using OrderedDicts but the use case still requires this sort of approach.

    Since dicts are not ordered you cannot directly do this. From your example, you are trying to reference the item like you would use a linked list.

    A quick solution would be instead to extract the keys and sort them then iterate over that list:

    keyList=sorted(d.keys())
    for i,v in enumerate(keyList):
        if v=='eeee':
            print d[keyList[i+1]]
            print d[keyList[i-1]]
    

    The keyList holds the order of your items and you have to go back to it to find out what the next/previous key is to get the next/previous value. You also have to check for i+1 being greater than the list length and i-1 being less than 0.

    You can use an OrderedDict similarly but I believe that you still have to do the above with a separate list as OrderedDict doesn't have next/prev methods.

    0 讨论(0)
提交回复
热议问题