2012-02-23 101 views
22

我必須合併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對SO,但其究竟合併2個字典。

+0

@SvenMarnach那太慷慨的你!完成。 + 1ed你的答案太:) – jerrymouse 2012-02-23 19:15:22

+0

**另請參見**:[合併嵌套字典](https://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge) – dreftymac 2017-12-11 21:03:39

+0

**另請參閱:** [類似的問題](https://stackoverflow.com/questions/2365921/merging-python-dictionaries) – dreftymac 2017-12-11 21:07:22

回答

24

可以遍歷直接的字典 - 無需使用range。字典的setdefault方法查找一個鍵,並返回值,如果找到。如果找不到,它將返回一個默認值,並將該默認值分配給該鍵。您可能會考慮使用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) 
2

這可以是多個比特優雅:

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) 

UPDATE:由改變通過斯文

建議

UPDATE:改變以避免重複(感謝的Marcin和史蒂芬)

+0

不錯。我建議'在dicts中爲d:'而不是'在xrange(len(dicts))中'。 – 2012-02-23 15:26:57

+1

@SvenMarnach尼斯,我做了這個改變。 – 2012-02-23 15:28:43

+1

小數點。重複鍵''c'':'[3,'foo',3]'。 OP的示例代碼顯示'3'不會重複。 – 2012-02-23 15:33:16

11

合併的鍵所有的密碼,併爲每個密鑰組裝的值列表:

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] 

e xpression set(k for d in dicts for k in d)構建了一組所有字典的唯一鍵。對於這些唯一鍵中的每一個,我們使用列表理解[d[k] for d in dicts if k in d]來構建此鍵的所有字典中的值列表。

既然你似乎只是一個每個鍵的獨特價值,您可能需要使用組,而不是:

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) 
+0

非常堅實。我認爲這可以通過解釋來改進。 – Edwin 2012-02-23 15:25:02

+0

@Edwin:謝謝,我補充了一些解釋。 – 2012-02-23 15:31:13

+0

@SvenMarnach次要的東西 - 第二個版本,我們得到一套詞典而不是一個列表字典 - 如果對OP很重要,很容易處理。 – 2012-02-23 16:00:10

3

永遠不要忘記的是,標準庫有豐富的工具,用於處理類型的字典和迭代:

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) 

注意,if v not in super_dict[k]可避免按照Steven Rumbalski的回答使用defaultdict(set)

17
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.iteritems(): 
     super_dict[k].add(v) 
+1

+1:這似乎正是問題所要求的(值中的獨特元素),以相對清晰且確定有效的方式完成(字典經歷了一次,而內置集合使得保留只有獨特的元素快)。 – EOL 2013-05-18 08:19:28

-2

我有點晚了比賽,但我有超越Python本身沒有依賴關係做了它在2號線:

flatten = lambda *c: (b for a in c for b in (flatten(*a) if isinstance(a, (tuple, list)) else (a,))) 
o = reduce(lambda d1,d2: dict((k, list(flatten([d1.get(k), d2.get(k)]))) for k in set(d1.keys() + d2.keys())), dicts) 
# output: 
# {'a': [1, 1, None], 'c': [3, 'foo', 3], 'b': [2, None, None], 'e': [None, 57], 'd': [None, 2, None]} 

但如果你不關心嵌套的列表,則:

o2 = reduce(lambda d1,d2: dict((k, [d1.get(k), d2.get(k)]) for k in set(d1.keys() + d2.keys())), dicts) 
# output: 
# {'a': [[1, 1], None], 'c': [[3, 'foo'], 3], 'b': [[2, None], None], 'e': [None, 57], 'd': [[None, 2], None]} 
+1

根據問題,不應創建「無」值。 – EOL 2013-05-18 08:23:03

1

對於oneliner,下面可以使用:

{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} 

雅可辯論的,但我個人更喜歡上了內涵循環。 :)

(字典,並設置內涵是提供Python 2.7/3.1和更新。)

-1

這似乎是大多數使用推導的答案是不是所有的可讀性。如果任何人在上面的答案混亂中迷失了,這可能會有所幫助(儘管非常遲到......)。只需循環每個字典的項目並將它們放在單獨的字典中即可。

super_dict = {key:val for d in dicts for key,val in d.items()} 
+1

OP要求保留'c''值:'{'c':[3,'foo']}'。 – 2016-07-21 15:09:21

0

我的解決方案類似於@senderle建議,但不是爲循環我用地圖

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