J'ai besoin de fusionner plusieurs dictionnaires, voici ce que j'ai par exemple:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}
Avec A
B
C
et D
étant les feuilles de l'arbre, comme{"info1":"value", "info2":"value2"}
Il y a un niveau inconnu (profondeur) de dictionnaires, cela pourrait être {2:{"c":{"z":{"y":{C}}}}}
Dans mon cas, il représente une structure de répertoires / fichiers avec les nœuds étant des documents et les feuilles étant des fichiers.
Je souhaite les fusionner pour obtenir:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
Je ne sais pas comment je pourrais le faire facilement avec Python.
y
aplatir auc
niveau ou quoi? Votre exemple est incomplet.Réponses:
c'est en fait assez délicat - en particulier si vous voulez un message d'erreur utile lorsque les choses sont incohérentes, tout en acceptant correctement les entrées dupliquées mais cohérentes (ce qu'aucune autre réponse ne fait ici ....)
en supposant que vous n'ayez pas un grand nombre d'entrées, une fonction récursive est la plus simple:
notez que cela mute
a
- le contenu deb
est ajouté àa
(qui est également retourné). si vous voulez garder,a
vous pouvez l'appeler comme çamerge(dict(a), b)
.agf a souligné (ci-dessous) que vous pouvez avoir plus de deux dictionnaires, auquel cas vous pouvez utiliser:
où tout sera ajouté à dict1.
[note - j'ai modifié ma réponse initiale pour faire muter le premier argument; qui rend le "réduire" plus facile à expliquer]
ps en python 3, vous aurez également besoin
from functools import reduce
la source
reduce
boucle ou une boucle équivalente pour travailler avec un nombre arbitraire dedict
s au lieu de deux. Cependant, je ne suis pas sûr que cela fasse ce qu'il veut non plus (il n'était pas clair), vous vous retrouvez avec2: {'c': {'z': {'y': {'info1': 'value', 'info2': 'value2'}}}, 'b': {'info1': 'value', 'info2': 'value2'}}
son deuxième exemple, je ne sais pas s'il veutz
ety
aplati ou pas?a[key] = a[key] + b[key]
. Merci pour la réponse utile.copy.deepcopy
.Voici un moyen simple de le faire à l'aide de générateurs:
Cela imprime:
la source
Un problème avec cette question est que les valeurs du dict peuvent être des éléments de données arbitrairement complexes. Sur la base de ces réponses et d'autres, j'ai trouvé ce code:
Mon cas d'utilisation est la fusion de fichiers YAML où je n'ai à gérer qu'un sous-ensemble de types de données possibles. Par conséquent, je peux ignorer les tuples et autres objets. Pour moi, une logique de fusion sensée signifie
Tout le reste et les imprévus entraînent une erreur.
la source
isinstance(a, (str, unicode, int, long, float))
isnt 'it?Comme il s'agit de la question canonique (en dépit de certaines non-généralités), je propose l'approche pythonique canonique pour résoudre ce problème.
Cas le plus simple: "les feuilles sont des dictionnaires imbriqués qui se terminent par des dictionnaires vides":
C'est le cas le plus simple pour la récursivité, et je recommanderais deux approches naïves:
Je crois que je préférerais le second au premier, mais gardez à l'esprit que l'état d'origine du premier devrait être reconstruit à partir de son origine. Voici l'utilisation:
Cas complexe: "les feuilles sont de tout autre type:"
Donc, s'ils se terminent par des dictionnaires, c'est un simple cas de fusion de la fin des dictionnaires vides. Sinon, ce n'est pas si banal. Si des chaînes, comment les fusionner? Les ensembles peuvent être mis à jour de la même manière, nous pourrions donc appliquer ce traitement, mais nous perdons l'ordre dans lequel ils ont été fusionnés. L'ordre est-il donc important?
Donc, au lieu de plus d'informations, l'approche la plus simple sera de leur donner le traitement de mise à jour standard si les deux valeurs ne sont pas des dictées: c'est-à-dire que la valeur du second dict écrasera la première, même si la valeur du second dict est None et la valeur du premier est un dict avec beaucoup d'informations.
Et maintenant
Retour
Application à la question initiale:
J'ai dû supprimer les accolades autour des lettres et les mettre entre guillemets simples pour que cela soit légitime Python (sinon, ils seraient définis comme des littéraux dans Python 2.7+) et ajouter une accolade manquante:
et
rec_merge(dict1, dict2)
retourne maintenant:Qui correspond au résultat souhaité de la question initiale (après modification, par exemple le
{A}
à'A'
.)la source
Basé sur @andrew cooke. Cette version gère les listes imbriquées de dictionnaires et permet également de mettre à jour les valeurs
la source
Cette procédure récursive simple fusionnera un dictionnaire dans un autre tout en remplaçant les clés en conflit:
Production:
la source
Basé sur les réponses de @andrew cooke. Il prend mieux en charge les listes imbriquées.
la source
Si vous avez un niveau inconnu de dictionnaires, alors je suggérerais une fonction récursive:
la source
Aperçu
L'approche suivante subdivise le problème d'une fusion profonde des dictionnaires en:
Une fonction de fusion superficielle paramétrée
merge(f)(a,b)
qui utilise une fonctionf
pour fusionner deux dictionnairesa
etb
Une fonction de fusion récursive
f
à utiliser avecmerge
la mise en oeuvre
Une fonction de fusion de deux dictionnaires (non imbriqués) peut être écrite de nombreuses manières. J'aime personnellement
Une bonne façon de définir une fonction de fusion récursive appropriée
f
consiste à utiliser multipledispatch qui permet de définir des fonctions qui évaluent le long de différents chemins en fonction du type de leurs arguments.Exemple
Pour fusionner deux dictionnaires imbriqués, utilisez simplement
merge(f)
par exemple:Remarques:
Les avantages de cette approche sont:
La fonction est construite à partir de fonctions plus petites qui font chacune une seule chose, ce qui rend le code plus simple à raisonner et à tester
Le comportement n'est pas codé en dur mais peut être modifié et étendu selon les besoins, ce qui améliore la réutilisation du code (voir l'exemple ci-dessous).
Personnalisation
Certaines réponses ont également considéré des dictionnaires contenant des listes, par exemple d'autres dictionnaires (potentiellement imbriqués). Dans ce cas, on peut vouloir cartographier les listes et les fusionner en fonction de la position. Cela peut être fait en ajoutant une autre définition à la fonction de fusion
f
:la source
Au cas où quelqu'un voudrait encore une autre approche à ce problème, voici ma solution.
Vertus : courtes, déclaratives et fonctionnelles dans le style (récursif, sans mutation).
Inconvénient potentiel : ce n'est peut-être pas la fusion que vous recherchez. Consultez la docstring pour la sémantique.
la source
Vous pouvez essayer mergedeep .
Installation
Usage
la source
Il y a un léger problème avec la réponse d'Andrew Cookes: dans certains cas, cela modifie le deuxième argument
b
lorsque vous modifiez le dict renvoyé. Plus précisément, c'est à cause de cette ligne:Si
b[key]
est adict
, il sera simplement attribué àa
, ce qui signifie que toute modification ultérieuredict
affectera à la foisa
etb
.Pour résoudre ce problème, la ligne devrait être remplacée par ceci:
Où
clone_dict
est:Encore. Cela ne tient évidemment pas compte
list
,set
et d'autres choses, mais j'espère que cela illustre les pièges lors de la tentative de fusiondicts
.Et par souci d'exhaustivité, voici ma version, où vous pouvez la passer plusieurs
dicts
:la source
deepcopy
au lieu declone_dict
?Cette version de la fonction prendra en compte N nombre de dictionnaires, et seulement les dictionnaires - aucun paramètre incorrect ne peut être passé, ou cela déclenchera une TypeError. La fusion elle-même tient compte des conflits de clés et, au lieu d'écraser les données d'un dictionnaire plus bas dans la chaîne de fusion, elle crée un ensemble de valeurs et les ajoute; aucune donnée n'est perdue.
Ce n'est peut-être pas le plus efficace sur la page, mais c'est le plus complet et vous ne perdrez aucune information lorsque vous fusionnerez vos 2 à N dictionnaires.
sortie: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}
la source
Étant donné que dictviews prend en charge les opérations d'ensemble, j'ai pu grandement simplifier la réponse de jterrace.
Toute tentative de combiner un dict avec un non dict (techniquement, un objet avec une méthode 'keys' et un objet sans méthode 'keys') lèvera une AttributeError. Cela inclut à la fois l'appel initial à la fonction et les appels récursifs. C'est exactement ce que je voulais alors je l'ai laissé. Vous pouvez facilement attraper un AttributeErrors levé par l'appel récursif et ensuite donner n'importe quelle valeur que vous voulez.
la source
Short-n-sweet:
Cela fonctionne comme (et est construit sur) la
dict.update
méthode de Python . Il retourneNone
(vous pouvez toujours ajouterreturn d
si vous préférez) car il met à jour le dictd
sur place. Les clésv
écrasent toutes les clés existantes dansd
(il n'essaye pas d'interpréter le contenu du dict).Il fonctionnera également pour d'autres mappages ("de type dict").
la source
Le code dépendra de vos règles de résolution des conflits de fusion, bien sûr. Voici une version qui peut prendre un nombre arbitraire d'arguments et les fusionne de manière récursive à une profondeur arbitraire, sans utiliser de mutation d'objet. Il utilise les règles suivantes pour résoudre les conflits de fusion:
{"foo": {...}}
a priorité sur{"foo": "bar"}
){"a": 1}
,{"a", 2}
et{"a": 3}
dans l' ordre, le résultat sera{"a": 3}
)la source
J'avais deux dictionnaires (
a
etb
) qui pouvaient chacun contenir n'importe quel nombre de dictionnaires imbriqués. Je voulais les fusionner récursivement, enb
prenant le pas sura
.Considérant les dictionnaires imbriqués comme des arbres, ce que je voulais était:
a
sorte que chaque chemin vers chaque feuille deb
soit représenté dansa
a
si une feuille est trouvée dans le chemin correspondant dansb
b
les nœuds feuilles restent des feuilles.Les réponses existantes étaient un peu compliquées à mon goût et laissaient quelques détails sur l'étagère. J'ai piraté ensemble ce qui suit, qui passe des tests unitaires pour mon ensemble de données.
Exemple (formaté pour plus de clarté):
Les chemins
b
qui devaient être entretenus étaient:1 -> 'b' -> 'white'
2 -> 'd' -> 'black'
3 -> 'e'
.a
avait les chemins uniques et non conflictuels de:1 -> 'a' -> 'red'
1 -> 'c' -> 'orange' -> 'dog'
ils sont donc toujours représentés dans la carte fusionnée.
la source
J'ai une solution itérative - fonctionne beaucoup mieux avec les gros dictionnaires et beaucoup d'entre eux (par exemple jsons, etc.):
notez que cela utilisera la valeur de d2 pour remplacer d1, au cas où ils ne seraient pas tous les deux. (identique à python
dict.update()
)quelques tests:
J'ai testé avec environ 1200 dictionnaires - cette méthode a pris 0,4 seconde, tandis que la solution récursive a pris environ 2,5 secondes.
la source
Cela devrait aider à fusionner tous les éléments de
dict2
dansdict1
:Veuillez le tester et nous dire si c'est ce que vous vouliez.
ÉDITER:
La solution mentionnée ci-dessus ne fusionne qu'un seul niveau, mais résout correctement l'exemple donné par OP. Pour fusionner plusieurs niveaux, la récursivité doit être utilisée.
la source
for k,v in dict2.iteritems(): dict1.setdefault(k,{}).update(v)
. Mais comme @agf l'a souligné, cela ne fusionne pas les dictionnaires imbriqués.{'a':'b'}
avec{'a':{'c':'d'}
).J'ai testé vos solutions et j'ai décidé d'utiliser celle-ci dans mon projet:
Passer des fonctions en tant que paramètres est la clé pour étendre la solution jterrace afin qu'elle se comporte comme toutes les autres solutions récursives.
la source
Le moyen le plus simple auquel je puisse penser est:
Production:
la source
J'ai une autre solution légèrement différente ici:
Par défaut, il résout les conflits en faveur des valeurs du second dict, mais vous pouvez facilement le remplacer, avec un peu de sorcellerie, vous pourrez même en supprimer des exceptions. :).
la source
la source
hé là j'ai aussi eu le même problème mais je pensais à une solution et je la posterai ici, au cas où cela serait également utile pour d'autres, en gros en fusionnant des dictionnaires imbriqués et en ajoutant également les valeurs, pour moi j'avais besoin de calculer certaines probabilités donc cela on a très bien fonctionné:
en utilisant la méthode ci-dessus, nous pouvons fusionner:
target = {'6,6': {'6,63': 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 1} , '6,63': {'63, 4 ': 1}}
src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1} }
et cela deviendra: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63' : 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 2},' 6,63 ': {'63, 4': 1 }}
notez également les changements ici:
cible = {'6,6': {'6,63': 1}, '6,63': {'63, 4 ': 1}, ' 4,4 ': {' 4,3 ': 1} , '63, 4 ': {' 4,4 ': 1}}
src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1} , '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}
merge = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63, 4 ': 1} , '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, ' 63,4 ': {' 4,4 ': 1}, ' 4,4 ': {' 4,3 ': 1,' 4,9 ': 1} }
n'oubliez pas d'ajouter également l'importation pour copie:
la source
Production:
la source
jetez un oeil au
toolz
packagedonne
la source
La fonction suivante fusionne b en a.
la source
Et juste une autre légère variation:
Voici une fonction de mise à jour approfondie basée sur un ensemble python3 pur. Il met à jour les dictionnaires imbriqués en parcourant un niveau à la fois et s'appelle lui-même pour mettre à jour chaque niveau suivant de valeurs de dictionnaire:
Un exemple simple:
la source
Et une autre réponse?!? Celui-ci évite également les mutations / effets secondaires:
la source