1. ホーム
  2. python

[解決済み] 辞書の辞書をマージする方法は?

2022-04-15 10:16:10

質問

複数の辞書をマージする必要があるのですが、例えばこんな感じです。

dict1 = {1:{"a":{A}}, 2:{"b":{B}}}

dict2 = {2:{"c":{C}}, 3:{"d":{D}}

A B CD のように、木の葉になっています。 {"info1":"value", "info2":"value2"}

辞書のレベル(深さ)は未知数であり、以下のようになります。 {2:{"c":{"z":{"y":{C}}}}}

私の場合は、ノードがdocs、リーフがfilesのディレクトリ/ファイル構造を表しています。

それらをマージして取得したい。

 dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}

Pythonでどうやったら簡単にできるのか、よくわからない。

どのように解決するのですか?

特に、重複しているが一貫性のあるエントリーを正しく受け入れながら、一貫性がない場合に有用なエラーメッセージを表示したい場合です (他の回答にはありません...)。

膨大な数のエントリーがないと仮定すれば、再帰的な関数が最も簡単です。

def merge(a, b, path=None):
    "merges b into a"
    if path is None: path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                merge(a[key], b[key], path + [str(key)])
            elif a[key] == b[key]:
                pass # same leaf value
            else:
                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
        else:
            a[key] = b[key]
    return a

# works
print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))
# has conflict
merge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})

を変異させることに注意してください。 a - の内容は b に追加されます。 a (も返されます)。もし a のように呼び出すことができます。 merge(dict(a), b) .

agf さんは、2 つ以上のディクショ ンを持つ可能性があることを (以下で) 指摘していますが、その場合は、これを使うことができます。

reduce(merge, [dict1, dict2, dict3...])

に追加されます。 dict1 .

備考 : 最初の回答を編集して、最初の引数をミューティングするようにしました。

PS : Python 3 では、以下のものも必要です。 from functools import reduce