web-dev-qa-db-fra.com

En Python, comment puis-je obtenir la clé suivante et la clé précédente: valeur d'une clé particulière dans un dictionnaire?

Ok, donc c'est un peu difficile à expliquer, mais voici:

J'ai un dictionnaire, auquel j'ajoute du contenu. Le contenu est un nom d’utilisateur haché (clé) avec une adresse IP (valeur). Je mettais les hachages dans un ordre en les lançant sur la base 16, puis en utilisant Collection.orderedDict. Ainsi, le dictionnaire ressemblait un peu à ceci:

d = {'1234': '8.8.8.8', '2345':'0.0.0.0', '3213':'4.4.4.4', '4523':'1.1.1.1', '7654':'1.3.3.7', '9999':'127.0.0.1'}

Il me fallait un mécanisme qui me permettrait de choisir l’une de ces clés et d’obtenir l’élément clé/valeur un plus haut et un plus bas. Ainsi, par exemple, si je choisis 2345, le code renverra les combinaisons clé: valeur '1234: 8.8.8.8' et '3213: 4.4.4.4'

Donc, quelque chose comme:

for i in d:
  while i < len(d)
   if i == '2345':
     print i.nextItem
     print i.previousItem
     break()
12
Brewer

Comme vous le voyez dans le code source OrderedDict , Si vous avez une clé et que vous voulez trouver le suivant et le précédent dans O(1), voici comment procéder.

>>> from collections import OrderedDict
>>> d = OrderedDict([('aaaa', 'a',), ('bbbb', 'b'), ('cccc', 'c'), ('dddd', 'd'), ('eeee', 'e'), ('ffff', 'f')])
>>> i = 'eeee'
>>> link_prev, link_next, key = d._OrderedDict__map['eeee']
>>> print 'nextKey: ', link_next[2], 'prevKey: ', link_prev[2]
nextKey:  ffff prevKey:  dddd

Cela vous donnera next et prev par ordre d'insertion. Si vous ajoutez des éléments dans un ordre aléatoire, gardez simplement une trace de vos éléments dans un ordre trié. 

4
jamylak

Vous pouvez utiliser une fonction générique, basée sur des itérateurs, pour obtenir une fenêtre en mouvement (tirée de 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

L'utilisation de la fonction ci-dessus avec OrderedDict.items() vous donnera trois paires (clé, valeur), dans l'ordre:

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

Bien sûr, en utilisant cette fonction, les première et dernière valeurs ne seront jamais en position médiane (même si cela ne devrait pas être difficile à faire avec certaines adaptations).

Je pense que le plus gros avantage est que cela ne nécessite pas de recherche de table dans les clés précédente et suivante, mais aussi qu’il est générique et fonctionne avec n’importe quelle valeur.

2
Rafael Lerm

Edit: OP indique maintenant qu’ils utilisent OrderedDicts mais le cas d’utilisation nécessite toujours ce type d’approche.

Puisque les dictés ne sont pas commandés, vous ne pouvez pas le faire directement. À partir de votre exemple, vous essayez de référencer l’élément comme si vous utilisiez une liste chaînée.

Une solution rapide consisterait plutôt à extraire les clés et à les trier, puis à les parcourir par-dessus cette liste:

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

La keyList conserve l'ordre de vos éléments et vous devez y revenir pour savoir quelle est la clé suivante/précédente pour obtenir la valeur suivante/précédente. Vous devez également vérifier si i + 1 est supérieur à la longueur de la liste et i-1 inférieur à 0.

Vous pouvez utiliser un OrderedDict de la même manière, mais je pense que vous devez toujours faire ce qui précède avec une liste séparée, car OrderedDict n’a pas de méthodes next/prev.

2
Adam Kerz

Vous pouvez également utiliser la méthode list.index().

Cette fonction est plus générique (vous pouvez vérifier les positions + n et -n), elle capturera les tentatives de recherche d'une clé qui n'est pas dans le dict, et retournera également None s'il n'y a rien avant ou après la clé:

def keyshift(dictionary, key, diff):
    if key in dictionary:
        token = object()
        keys = [token]*(diff*-1) + sorted(dictionary) + [token]*diff
        newkey = keys[keys.index(key)+diff]
        if newkey is token:
            print None
        else:
            print {newkey: dictionary[newkey]}
    else:
        print 'Key not found'


keyshift(d, 'bbbb', -1)
keyshift(d, 'eeee', +1)
1
Roberto

Essayer:

pos = 0
d = {'aaaa': 'a', 'bbbb':'b', 'cccc':'c', 'dddd':'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])

Comme dans la réponse de @ AdamKerz, enumerate semble Pythonic, mais si vous êtes débutant, ce code peut vous aider à le comprendre facilement.

Et je pense que son plus rapide + plus petit par rapport au tri suivi par la liste de construction et ensuite énumérer

1
RinkyPinku

C’est peut-être un peu exagéré, mais vous pouvez garder la trace des clés insérées avec une classe d’aide et, conformément à cette liste, vous pouvez récupérer la clé pour Previous ou Next. N'oubliez pas de vérifier les conditions de bordure si l'objet est déjà le premier ou le dernier élément. De cette façon, vous n'aurez pas besoin de toujours recourir à la liste ordonnée ou à la recherche de l'élément.

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', 'dddd':'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
1
user1767754

Vous pouvez stocker les clés et les valeurs dans la variable temp précédemment, et accéder à la paire valeur/clé précédente et suivante à l'aide de l'index. 

C'est assez dynamique, fonctionnera pour n'importe quelle clé que vous interrogez. Veuillez vérifier ce code:

d = {'1234': '8.8.8.8', '2345':'0.0.0.0', '3213':'4.4.4.4', '4523':'1.1.1.1', '7654':'1.3.3.7', '9999':'127.0.0.1'}
ch = raw_input('Pleasure Enter your choice : ')
keys = d.keys()
values = d.values()
#print keys, values
for k,v in d.iteritems():
    if k == ch:
        ind = d.keys().index(k)
        print keys[ind-1], ':',values[ind-1]
        print keys[ind+1], ':',values[ind+1]
1
user123

Je pense que c’est une bonne méthode pythonique de résoudre votre problème en utilisant une compréhension de lambda et list , bien que cela puisse ne pas être optimal en temps d’exécution:

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
Russell