web-dev-qa-db-ja.com

いくつかのpython辞書をマージ

python辞書のリストをマージする必要があります。たとえば:

dicts[0] = {'a':1, 'b':2, 'c':3}
dicts[1] = {'a':1, 'd':2, 'c':'foo'}
dicts[2] = {'e':57,'c':3}

super_dict = {'a':[1], 'b':[2], 'c':[3,'foo'], 'd':[2], 'e':[57]}    

次のコードを書きました。

super_dict = {}
for d in dicts:
    for k, v in d.items():
        if super_dict.get(k) is None:
            super_dict[k] = []
        if v not in super_dict.get(k):
            super_dict[k].append(v)

よりエレガントに/最適化して提示できますか?

別の question on SOですが、正確に2つの辞書をマージすることについてです。

36
jerrymouse

辞書を直接反復処理できます。rangeを使用する必要はありません。 dictのsetdefaultメソッドはキーを検索し、見つかった場合は値を返します。見つからない場合は、デフォルトを返し、そのデフォルトをキーに割り当てます。

super_dict = {}
for d in dicts:
    for k, v in d.iteritems():  # d.items() in Python 3+
        super_dict.setdefault(k, []).append(v)

また、defaultdictの使用を検討することもできます。これは、キーが見つからない場合にデフォルト値を返す関数を呼び出すことで、setdefaultを自動化します。

import collections
super_dict = collections.defaultdict(list)
for d in dicts:
    for k, v in d.iteritems():  # d.items() in Python 3+
        super_dict[k].append(v)

また、 Sven Marnach がはっきりと観察されているように、リスト内の値の重複を避けたいようです。その場合、setはあなたが欲しいものを取得します:

import collections
super_dict = collections.defaultdict(set)
for d in dicts:
    for k, v in d.iteritems():  # d.items() in Python 3+
        super_dict[k].add(v)
32
senderle
from collections import defaultdict

dicts = [{'a':1, 'b':2, 'c':3},
         {'a':1, 'd':2, 'c':'foo'},
         {'e':57, 'c':3} ]

super_dict = defaultdict(set)  # uses set to avoid duplicates

for d in dicts:
    for k, v in d.items():  # use d.iteritems() in python 2
        super_dict[k].add(v)
25

すべての辞書のキーをマージし、キーごとに値のリストを組み立てます。

_super_dict = {}
for k in set(k for d in dicts for k in d):
    super_dict[k] = [d[k] for d in dicts if k in d]
_

set(k for d in dicts for k in d)は、すべての辞書のすべての一意のキーのセットを構築します。これらの一意のキーごとに、リスト内包表記_[d[k] for d in dicts if k in d]_を使用して、このキーのすべてのdictから値のリストを作成します。

各キーのnique値は1つだけのように見えるため、代わりにセットを使用することもできます。

_super_dict = {}
for k in set(k for d in dicts for k in d):
    super_dict[k] = set(d[k] for d in dicts if k in d)
_
12
Sven Marnach

標準ライブラリには、辞書と反復を処理するための豊富なツールがあることを決して忘れないでください。

_from itertools import chain
from collections import defaultdict
super_dict = defaultdict(list)
for k,v in chain.from_iterable(d.iteritems() for d in dicts):
    if v not in super_dict[k]: super_dict[k].append(v)
_

Steven Rumbalskiの答えに従って、defaultdict(set)を使用することで_if v not in super_dict[k]_を回避できることに注意してください。

3
Marcin

キーの値がリストにある場合:

from collections import defaultdict

    dicts = [{'a':[1], 'b':[2], 'c':[3]},
             {'a':[11], 'd':[2], 'c':['foo']},
             {'e':[57], 'c':[3], "a": [1]} ]

super_dict = defaultdict(list)  # uses set to avoid duplicates

for d in dicts:
    for k, v in d.items():  # use d.iteritems() in python 2
        super_dict[k] = list(set(super_dict[k] + v))

combined_dict = {}

for elem in super_dict.keys():
    combined_dict[elem] = super_dict[elem]

combined_dict
## output: {'a': [1, 11], 'b': [2], 'c': [3, 'foo'], 'd': [2], 'e': [57]}
3
Ramkrishan Sahu

これはもう少しエレガントかもしれません:

super_dict = {}
for d in dicts:
    for k, v in d.iteritems():
        l=super_dict.setdefault(k,[])
        if v not in l:
            l.append(v)

更新:Svenによって提案された変更を行いました

更新:重複を避けるために変更(MarcinとStevenに感謝)

2
Vaughn Cato

ワンライナーの場合、次を使用できます。

{key: {d[key] for d in dicts if key in d} for key in {key for d in dicts for key in d}}

ただし、組み合わせたキーセットに名前を付けると読みやすくなります。

combined_key_set = {key for d in dicts for key in d}
super_dict = {key: {d[key] for d in dicts if key in d} for key in combined_key_set}

優雅さは議論の余地がありますが、個人的にはforループよりも理解が好きです。 :)

(辞書とセット内包表記は Python 2.7/3.1 以降で利用可能です。)

1
7mp

まだ誰も投稿していないのではないかと思います。

d = {**one, **two, **three, **four}
print d

これで十分でしょう。

0
Kuldeep Gajera

私の解決策は提案された@senderleに似ていますが、forループの代わりにmapを使用しました

super_dict = defaultdict(set)
map(lambda y: map(lambda x: super_dict[x].add(y[x]), y), dicts)
0
MosheZada

関心のあるキーが同じネストレベルにあると想定する場合、各辞書を再帰的に走査し、そのキーを使用して新しい辞書を作成し、それらを効果的にマージできます。

merged = {}
for d in dicts:
    def walk(d,merge):
        for key, item in d.items():
            if isinstance(item, dict):
                merge.setdefault(key, {})
                walk(item, merge[key])
            else:
                merge.setdefault(key, [])
                merge[key].append(item)
    walk(d,merged)

たとえば、マージする次の辞書があるとします。

dicts = [{'A': {'A1': {'FOO': [1,2,3]}}},
         {'A': {'A1': {'A2': {'BOO': [4,5,6]}}}},
         {'A': {'A1': {'FOO': [7,8]}}},
         {'B': {'B1': {'COO': [9]}}},
         {'B': {'B2': {'DOO': [10,11,12]}}},
         {'C': {'C1': {'C2': {'POO':[13,14,15]}}}},
         {'C': {'C1': {'ROO': [16,17]}}}]

各レベルでキーを使用すると、次のようになります。

{'A': {'A1': {'FOO': [[1, 2, 3], [7, 8]], 
              'A2': {'BOO': [[4, 5, 6]]}}},
 'B': {'B1': {'COO': [[9]]}, 
       'B2': {'DOO': [[10, 11, 12]]}},
 'C': {'C1': {'C2': {'POO': [[13, 14, 15]]}, 
              'ROO': [[16, 17]]}}}

注:各ブランチのリーフは何らかの種類のリストであると想定していますが、状況に応じてロジックを変更して、必要な処理を実行できます。

0
davini

Defaultdictの使用は適切ですが、これはitertools.groupbyを使用して行うこともできます。

import itertools
# output all dict items, and sort them by key
dicts_ele = sorted( ( item for d in dicts for item in d.items() ), key = lambda x: x[0] )
# groups items by key
ele_groups = itertools.groupby( dicts_ele, key = lambda x: x[0] )
# iterates over groups and get item value
merged = { k: set( v[1] for v in grouped ) for k, grouped in ele_groups }

そして明らかに、このコードブロックを1行のスタイルにマージできます。

merged = {
    k: set( v[1] for v in grouped )
    for k, grouped in (
        itertools.groupby(
            sorted(
                ( item for d in dicts for item in d.items() ),
                key = lambda x: x[0]
            ),
            key = lambda x: x[0]
        )
    )
}
0
Sphynx-HenryAY