Récursive diff de deux dictionnaires python (les clés et les valeurs)
J'ai donc un dictionnaire python, appeler d1
, et une version de ce dictionnaire à un point plus tard dans le temps, de l'appeler d2
. Je veux trouver tous les changements entre d1
et d2
. En d'autres termes, tout ce qui a été ajouté, supprimé ou modifié. Le problème est que les valeurs peuvent être des entiers, des chaînes, des listes ou des dicts, donc il doit être récursive. C'est ce que j'ai à ce jour:
def dd(d1, d2, ctx=""):
print "Changes in " + ctx
for k in d1:
if k not in d2:
print k + " removed from d2"
for k in d2:
if k not in d1:
print k + " added in d2"
continue
if d2[k] != d1[k]:
if type(d2[k]) not in (dict, list):
print k + " changed in d2 to " + str(d2[k])
else:
if type(d1[k]) != type(d2[k]):
print k + " changed to " + str(d2[k])
continue
else:
if type(d2[k]) == dict:
dd(d1[k], d2[k], k)
continue
print "Done with changes in " + ctx
return
Il fonctionne très bien, sauf si la valeur est une liste de. Je ne peux pas tout à fait avec une élégante façon de traiter avec les listes, sans avoir une énorme, une version légèrement révisée de cette fonction répété après un if(type(d2) == list)
.
Toutes les pensées?
EDIT: Ce qui diffère de l' ce post parce que les touches peuvent changer
- Exemple:
list1 = [0, 1, 2, 3, 4, 5, 6, 7]
,list2 = [0, 2, 3, 4, 5, 6, 7, 8]
. Ce que la sortie ne vous attendez-vous? - Si ils étaient sous la même clé dans 2 différents dicts, je pense que: 1 supprimé; 8 ajouté (sous la même clé). Si ils étaient sous différentes touches, puis ils sont de différents éléments.
- Cela peut rapidement devenir difficile. N'est de l'ordre de la matière? Que faire si
8
est déplacé vers l'avant:[8, 1, 2, 3, 4, 5, 6, 7]
, qui n'ordonne pas pris en compte, ou seulement de la présence/absence (un ensemble)? La liste contient un imbriquée dictionnaire, qui contient à son tour une liste, etc? - Pouvez-vous donner un exemple de la sortie échoue sur?
- Yup. Les listes peuvent contenir des dictionnaires, qui ne peut en contenir.... ses tortues tout le chemin vers le bas. Je n'ai pas vraiment besoin de tuples, mais à ce point, qui ne vous aide pas beaucoup
- sûr: d1={"name":"Joe", "Animaux de compagnie":[{"name":"point", "espèces":"chien"}]}; d2={"name":"Joe", "Animaux de compagnie":[{"name":"point", "espèces":"chat"}]}
- Juste par curiosité, Pourquoi la profusion de types? Serait-il possible qu'il existe une meilleure structure de données pour votre problème?
- Je suis le stockage de ces dicts dans mongodb, et l'application doit être en mesure de pousser des objets imbriqués dans des objets
Vous devez vous connecter pour publier un commentaire.
Une option serait de convertir toutes les listes que vous exécutez dans les dictionnaires avec l'index comme une clé. Par exemple:
Voici les résultats avec l'échantillon de dictionnaires que vous avez donné dans les commentaires:
Noter que ce sera de comparer indice par indice, donc, il aura besoin de quelques modifications de bien travailler pour la liste des éléments ajoutés ou supprimés.
Dans le cas où vous souhaitez que la différence de manière récursive, j'ai écrit un forfait pour python:
https://github.com/seperman/deepdiff
Installation
Installer depuis PyPi:
Exemple d'utilisation
Importation
Même objet retourne vide
Type d'un élément a changé
Valeur d'un élément a changé
Élément ajouté ou supprimé,
Chaîne différence
Chaîne de différence 2
Changement de Type de
Liste différence
Liste différence 2:
Liste différence en ignorant l'ordre ou de doublons: (avec les mêmes dictionnaires comme ci-dessus)
Liste qui contient le dictionnaire:
Ensembles:
Nommé Tuples:
Objets personnalisés:
Attribut de l'objet ajouté:
str(DeepDiff(t1, t2)) == "{}"
? Tout ce que je besoin de savoir si elles sont égales ou pas...>>> DeepDiff(1,1) {} >>> not bool(DeepDiff(1,1)) True
assertTrue(DeepDiff(result,expected_result).are_equal)
)assertDeepTrue(result, expected_result)
? Je l'avais écrit pour moi, mais je peux l'ajouter à DeepDiff de sorte que vous pouvez l'utiliser.Juste une pensée: Vous pouvez essayer une approche orientée-objet où vous tirer de votre propre dictionnaire de la classe qui conserve la trace de toutes les modifications apportées à celle-ci (et les rapports entre eux). Semble que cela pourrait avoir de nombreux avantages sur d'essayer de comparer les deux dicts...on est noté à la fin.
Pour montrer comment ça pourrait être fait, voici une assez complet et très peu testé exemple de mise en œuvre qui devrait fonctionner avec Python 2 et 3:
Note que, contrairement à une simple comparaison de la avant et après état d'un dictionnaire, cette classe va vous parler de touches qui ont été ajoutés, puis supprimé en d'autres termes, il conserve un historique complet jusqu'à sa
_changelist
est désactivée.De sortie:
Votre fonction doit commencer par vérifier le type de ses arguments, écrire la fonction de sorte qu'il peut gérer des listes, des dictionnaires, des entiers et des chaînes de caractères. De cette façon, vous n'avez pas à dupliquer quelque chose, il vous suffit d'appeler récursivement.
Psuedocode:
Envisager d'utiliser
hasattr(obj, '__iter__')
comme vous le répète à travers l'objet. Si un objet implémente l'__iter__
méthode, vous savez que vous pouvez effectuer une itération sur elle.Comme suggéré par Serge, j'ai trouvé cette solution utile pour avoir un rapide retour boolean si les deux dictionnaires match "tout en bas":
Voici une mise en œuvre inspirée par Winston Ewert
sera de retour:
Il est amusant de faire quelque chose de vous-mêmes à la pratique et à apprendre, mais je trouve que pour des tâches non négligeables, le prêt et maintenu packages souvent mieux travailler.
Envisager de convertir en json et d'utiliser certaines décent "sémantique" json comparateur de dire https://www.npmjs.com/package/compare-json ou en ligne http://jsondiff.com. Aurait besoin stringify numéro de clé.
Si vous pouvez essayer de traduire jsondiff de python si vous avez vraiment besoin.
La Conversion de JavaScript en code Python?