4
votes

Comment mettre à jour les valeurs dans le dictionnaire imbriqué si les clés sont dans une liste?

Disons que j'ai une liste de clés

{
"key1": {
    "key2": {
        "key3": "some_value"
        }
    },
}

et que j'ai une valeur

value = "my_value"

et un exemple dict my_dict avec cette structure

key_lst = ["key1", "key2", "key3"]

Comment puis-je attribuer dynamiquement la nouvelle valeur de la variable value à my_dict ["key1"] ["key2"] ["key3"] en passant par / en boucle sur mon key_lst?

Je ne peux pas simplement dire my_dict ["key1" ] ["key2"] ["key3"] = valeur car les clés et le nombre de clés changent. J'obtiens toujours les clés (le chemin dans lequel je dois enregistrer la valeur) dans une liste ...

Le résultat que je recherche est {'key1': {'key2': {'key3': 'ma_valeur'}}} . La structure du dictionnaire est prédéfinie.

J'utilise Python 3.7


0 commentaires

5 Réponses :


0
votes

Je suppose que vous pouvez parcourir vos clés comme ceci:

d = {}
a = d
for i in key_lst: 
    a[i] = {}
    if i == key_lst[-1]:
        a[i] = value
    else:
        a = a[i]
print(d)
# {'key1': {'key2': {'key3': 'my_value'}}}

Edit: Je suppose que j'ai mal lu la question et répondu comme si le dictionnaire n'existait pas déjà. La réponse jpp est plutôt chouette sinon je suppose!


0 commentaires

0
votes

Voici ce que vous voulez:

from collections import defaultdict

nest = lambda: defaultdict(nest)
d = nest()

def update(d, key_lst , val):
    for k in key_lst[:-1]:
        d = d[k]
    d[key_lst[-1]] = val

update(d, 'qwer', 0)

Vous pouvez aussi utiliser defaultdict , c'est bien dans un sens mais imprime plutôt moche ...:

def update(d, key_lst , val):
    for k in key_lst[:-1]:
        if k not in d:
            d[k] = {}
        d = d[k]
    d[key_lst[-1]] = val

d = {}

update(d, list('qwer'), 0)
# d = {'q': {'w': {'e': {'r': 0}}}}


0 commentaires

5
votes

Vous pouvez créer / accéder de manière itérative aux niveaux en utilisant setdefault dans une boucle:

d = {}
d2 = d
for k in key_lst[:-1]:
    d2 = d2.setdefault(k, {})

d2[key_lst[-1]] = value
print(d)
# {'key1': {'key2': {'key3': 'my_value'}}}

d est la référence à votre dictionnaire, et d2 est une référence jetable qui accède aux niveaux internes à chaque itération.


1 commentaires

Oui fonctionne parfaitement. Impressionnant.



7
votes

Structure de dictionnaire prédéfinie: functools.reduce code >

Vous pouvez définir une fonction en utilisant functools.reduce pour appliquer getitem à plusieurs reprises, puis définir une valeur fournie:

from collections import defaultdict

def dd_rec():
    return defaultdict(dd_rec)

def defaultify(d):
    if not isinstance(d, dict):
        return d
    return defaultdict(dd_rec, {k: defaultify(v) for k, v in d.items()})

dd = defaultify(d)

key_lst = ["key1", "key2", "key5", "key6"]
value = "my_value2"
dd = set_nested_item(dd, key_lst, value)

print(dd)

# defaultdict(<function __main__.<lambda>>,
#             {'key1': defaultdict(<function __main__.<lambda>>,
#                          {'key2': defaultdict(<function __main__.<lambda>>,
#                                       {'key3': 'my_value',
#                                        'key5': defaultdict(<function __main__.<lambda>>,
#                                                    {'key6': 'my_value2'})})})})

Remarque operator.getitem est utilisé pour accéder à dict .__ getitem__ , ou à son sucre syntaxique plus couramment utilisé dict [] . Dans ce cas, functools.reduce appelle getitem de manière récursive sur dataDict , en utilisant successivement chaque valeur de mapList [: - 1] code> comme argument. Avec [: -1] , nous omettons intentionnellement la dernière valeur, nous pouvons donc utiliser __setitem__ via dict [key] = valeur pour le clé finale.


Imbrication arbitraire du dictionnaire: collections.defaultdict

Si vous souhaitez ajouter des éléments à des branches arbitraires non encore définies, vous pouvez construire un defaultdict . Pour cela, vous pouvez d'abord defaultify votre entrée de dictionnaire habituelle, puis utiliser set_nested_item comme avant:

from functools import reduce
from operator import getitem

def set_nested_item(dataDict, mapList, val):
    """Set item in nested dictionary"""
    reduce(getitem, mapList[:-1], dataDict)[mapList[-1]] = val
    return dataDict

key_lst = ["key1", "key2", "key3"]
value = "my_value"
d = {"key1": {"key2": {"key3": "some_value"}}}

d = set_nested_item(d, key_lst, value)

print(d)
# {'key1': {'key2': {'key3': 'my_value'}}}

5 commentaires

C'est une réponse plutôt cool. Voulez-vous m'expliquer un peu plus sur votre fonction set_nested_item ?


Mais cela suppose que la structure particulière existe déjà. Et la définition de nouvelles valeurs? Vous aurez besoin d'un defaultdict, non? Cela ne peut pas gérer les KeyErrors tels quels.


@hqkhan, a ajouté une explication.


@coldspeed, d'accord, cela ne gère pas KeyError . Cela dit, je ne sais pas si cela est pertinent pour les exigences d'OP. Par exemple, il est concevable que la structure du dictionnaire soit donnée et que vous souhaitiez uniquement définir des valeurs; par conséquent, KeyError est ce que vous voulez .


JÉSUS CHRIST! C'est une bonne solution. La structure de dictée est en fait donnée et je m'attendrais à un KeyError si la clé n'est pas là. BTW: Pas une exigence mais c'est plus de trois fois plus rapide que la réponse de @ coldspeed. C'est celui-là, merci.



0
votes
key_lst = ["key1", "key2", "key3"]
my_dict={
"key1": {
    "key2": {
        "key3": "some_value"
        }
    },
}

val=my_dict
#loop gets second to last key in chain(path) and assigns it to val
for x in key_lst[:-1]:
    val=val[x]
#now we can update value of last key, cause dictionary key is passed by reference
val[key_lst[-1]]="new value"

print (my_dict)

#{'key1': {'key2': {'key3': 'new value'}}}

6 commentaires

@Julien qu'est-ce qui est à moitié cassé?


@Julien c'est vrai merci, mais je ne vois pas la possibilité d'une clé non existante en question


Je reprends ça. OP a clarifié que la structure du dict est donnée dans un commentaire à une autre réponse ...


@Julien merci, je pensais supprimer ma réponse, vous savez que je suis nouveau et j'ai été en retard pour soumettre cette réponse et je ne savais pas qu'il y avait une réponse similaire. la prochaine fois, je soumettrai soigneusement :)


Veuillez ajouter quelques commentaires autour de lui expliquant pourquoi c'est une réponse et / ou quel problème ce code résout.


@ Commentaires Kraang-Prime ajoutés