116

I have multiple dicts/key-value pairs like this:

d1 = {key1: x1, key2: y1}  
d2 = {key1: x2, key2: y2}  

I want the result to be a new dict (in most efficient way, if possible):

d = {key1: (x1, x2), key2: (y1, y2)}  

Actually, I want result d to be:

d = {key1: (x1.x1attrib, x2.x2attrib), key2: (y1.y1attrib, y2.y2attrib)}  

If somebody shows me how to get the first result, I can figure out the rest.

everestial007
  • 5,482
  • 7
  • 28
  • 59
Salil
  • 8,959
  • 8
  • 41
  • 56

20 Answers20

96

Here's a general solution that will handle an arbitrary amount of dictionaries, with cases when keys are in only some of the dictionaries:

from collections import defaultdict

d1 = {1: 2, 3: 4}
d2 = {1: 6, 3: 7}

dd = defaultdict(list)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key].append(value)

print(dd)

Shows:

defaultdict(<type 'list'>, {1: [2, 6], 3: [4, 7]})

Also, to get your .attrib, just change append(value) to append(value.attrib)

Simba
  • 17,532
  • 3
  • 48
  • 64
Eli Bendersky
  • 248,051
  • 86
  • 340
  • 401
  • 1
    I think the OP wants the values as `tuple` not `list`. – user225312 May 10 '11 at 07:08
  • 1
    @A A: does it really matter? tuples will be more tricky to build in the more general case of multiple input dicts where some keys present not everywhere, imho – Eli Bendersky May 10 '11 at 07:10
  • 1
    You may then want to make a normal `dict` out of the `defaultdict` so you have normal `dict` behavior for non-existent keys etc: `dd = dict(dd)` – Ned Deily May 10 '11 at 07:12
  • @Ned: good point, but it depends on the eventual use of the data – Eli Bendersky May 10 '11 at 07:14
  • 1
    @Eli: No it doesn't matter but I was just trying to base it on what the OP wanted and was hoping that there would be a solution for tuples from you :-) – user225312 May 10 '11 at 07:28
  • @A A: since tuples are immutable, they're not really well suited for this idiom with `defaultdict`, so no luck :) – Eli Bendersky May 10 '11 at 07:31
  • @Eli, my attribs are different for different dicts i.e. d1[key1].x1attrib and d2[key1].x2attrib – Salil May 10 '11 at 08:42
  • @Salil: that's a different question, then. You should explain thoroughly what attribs are used for what dicts - since you say there are *multiple* dicts – Eli Bendersky May 10 '11 at 09:27
  • This works if you want a dict of tuples: `dd[key] += (value,)` (for use with `dd = defaultdict(tuple)` – Sean McCarthy Feb 11 '22 at 04:57
59

assuming all keys are always present in all dicts:

ds = [d1, d2]
d = {}
for k in d1.iterkeys():
    d[k] = tuple(d[k] for d in ds)

Note: In Python 3.x use below code:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = tuple(d[k] for d in ds)

and if the dic contain numpy arrays:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = np.concatenate(list(d[k] for d in ds))
blubb
  • 9,023
  • 2
  • 38
  • 74
  • 3
    Just "for k in d1" would do I think. – Salil May 10 '11 at 08:38
  • and d.get(k, None) in place of d[k] – tahir May 23 '13 at 14:05
  • 1
    @tahir This would mean that dicts have non-matching keys so iterating over `d1` is not correct (it may miss keys in other dicts). – yugr Sep 01 '18 at 17:26
  • 1
    For python 3 users: d1.iterkeys() =d1.items() – Riley Sep 07 '18 at 07:35
  • It still doesn't work for me in Python3.x. I've tried this even if my values are not arrays, and it works. However, the values output will be arrays. https://stackoverflow.com/questions/54040858/python-3-x-merge-two-dictionaries-with-same-keys-and-values-being-array – Ric S Jan 08 '19 at 21:18
5
dict1 = {'m': 2, 'n': 4}
dict2 = {'n': 3, 'm': 1}

Making sure that the keys are in the same order:

dict2_sorted = {i:dict2[i] for i in dict1.keys()}

keys = dict1.keys()
values = zip(dict1.values(), dict2_sorted.values())
dictionary = dict(zip(keys, values))

gives:

{'m': (2, 1), 'n': (4, 3)}
Mahdi Ghelichi
  • 941
  • 12
  • 23
  • 2
    Order of elements in `values()` is undefined so you may be merging values from unrelated keys. – yugr Sep 01 '18 at 16:18
  • I just applied the changes so it can now capture your feedback – Mahdi Ghelichi Sep 03 '18 at 15:09
  • I don't think the change will fix the issue. You need to use `sorted(d.items())` or `sorted(d.keys())` to achieve predictable results. – yugr Sep 04 '18 at 08:17
  • Can you give an example that prove it otherwise? dict2_sorted is a sorted dictionary in python ! – Mahdi Ghelichi Sep 04 '18 at 13:58
  • The fact that it happens to work for small dictionaries on your machine is not a proof. Check repro [here](https://gist.github.com/yugr/e1d979c2dfdbbc51a782f3100f0dbf05). – yugr Sep 04 '18 at 16:27
  • I looked at your little example. The outcomes of lines 9 and 14 are exactly the same for me. Also, you are not sorting one dict based on the keys of another dict (so your example is completely irrelevant and unproducible) – Mahdi Ghelichi Sep 04 '18 at 16:56
  • "The outcomes of lines 9 and 14 are exactly the same for me" - I tried two different online interpreters ([this](https://www.onlinegdb.com/online_python_interpreter) and [that](https://www.tutorialspoint.com/execute_python_online.php)) and in both cases output was different. It's different on my machine as well... – yugr Sep 04 '18 at 17:56
  • "Also, you are not sorting one dict based on the keys of another dict" - my point is that line which creates `d_sorted` does not really "sort" anything - order of elements in Python dictionary is undefined by design. See [this](https://gist.github.com/yugr/c65241789604ba73b0ee280a81d492fe) example which is closer to what you wrote. I ran it in [this](https://www.onlinegdb.com/online_python_interpreter) interpreter. – yugr Sep 04 '18 at 18:02
  • I use PyCharm and not some online interpreters. I do not want to sort anything. I want to sort one dict with respect to another dict. I do not care about the alphabetical other of the other dict's keys ! According to your argument, I need to sort both dicts, that way it works. But, why should I sort two dicts (alphabetically) when I can sort one with respect to the other and just zip them both (the keys are aligned in both cases). – Mahdi Ghelichi Sep 04 '18 at 19:39
  • "I use PyCharm and not some online interpreters" - online interpreters are just web frontends to real Python interpreters running somewhere. My point stays that different Python versions will give you different results. – yugr Sep 05 '18 at 07:38
  • "I can sort one with respect to the other" - no, this is not possible in Python as demonstrated by the second example I posted. Expression which you use to compute `dict2_sorted` does _not_ guarantee that it's values will go in the same order as values in `dict11`. – yugr Sep 05 '18 at 07:38
  • Please provide me an example in which the order of keys are not the same as the other dict once aligned in the way that I aligned. – Mahdi Ghelichi Sep 05 '18 at 14:18
  • 1
    I did a small research on this. In recent versions of Python (3.6+) iteration order started to match insertion order (see e.g. [here](https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation)) which makes your code pass. But this is considered to be an implementation detail which shouldn't be relied on. My second example (see [here](https://gist.github.com/yugr/c65241789604ba73b0ee280a81d492fe)) reliably fails in [onlinegdb](https://www.onlinegdb.com/online_python_debugger) which uses old Python 3.4. Other online interpreters use newer Pythons so issue can not be reproduced there. – yugr Sep 05 '18 at 15:07
  • I don't think this would work if the 2 dictionaries don't have the exact same keys. – hostingutilities.com Feb 11 '20 at 06:19
5

Here is one approach you can use which would work even if both dictonaries don't have same keys:

d1 = {'a':'test','b':'btest','d':'dreg'}
d2 = {'a':'cool','b':'main','c':'clear'}

d = {}

for key in set(d1.keys() + d2.keys()):
    try:
        d.setdefault(key,[]).append(d1[key])        
    except KeyError:
        pass

    try:
        d.setdefault(key,[]).append(d2[key])          
    except KeyError:
        pass

print d

This would generate below input:

{'a': ['test', 'cool'], 'c': ['clear'], 'b': ['btest', 'main'], 'd': ['dreg']}
posdef
  • 6,366
  • 11
  • 43
  • 90
sateesh
  • 26,733
  • 7
  • 36
  • 45
  • Can `set(d1.keys() + d2.keys())` be changed to `set(list(d1.keys()) + list(d2.keys()))` in the answer (for Python 3.x)? Otherwise it will throw a `TypeError: unsupported operand type(s) for +: 'dict_keys' and 'dict_keys'` error, in python3.x – R4444 Dec 17 '18 at 19:20
4

This function merges two dicts even if the keys in the two dictionaries are different:

def combine_dict(d1, d2):
    return {
        k: tuple(d[k] for d in (d1, d2) if k in d)
        for k in set(d1.keys()) | set(d2.keys())
    }

Example:

d1 = {
    'a': 1,
    'b': 2,
}
d2` = {
    'b': 'boat',
    'c': 'car',
}
combine_dict(d1, d2)
# Returns: {
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',)
# }
Flux
  • 7,901
  • 5
  • 33
  • 76
4

If you only have d1 and d2,

from collections import defaultdict

d = defaultdict(list)
for a, b in d1.items() + d2.items():
    d[a].append(b)
riza
  • 14,948
  • 7
  • 28
  • 29
1

Python 3.x Update

From Eli Bendersky answer:

Python 3 removed dict.iteritems use dict.items instead. See Python wiki: https://wiki.python.org/moin/Python3.0

from collections import defaultdict

dd = defaultdict(list)

for d in (d1, d2):
    for key, value in d.items():
        dd[key].append(value)
candid
  • 189
  • 1
  • 13
1

Assume that you have the list of ALL keys (you can get this list by iterating through all dictionaries and get their keys). Let's name it listKeys. Also:

  • listValues is the list of ALL values for a single key that you want to merge.
  • allDicts: all dictionaries that you want to merge.
result = {}
for k in listKeys:
    listValues = [] #we will convert it to tuple later, if you want.
    for d in allDicts:
       try:
            fileList.append(d[k]) #try to append more values to a single key
        except:
            pass
    if listValues: #if it is not empty
        result[k] = typle(listValues) #convert to tuple, add to new dictionary with key k
Mahfuza Mohona
  • 103
  • 3
  • 12
Long
  • 1,154
  • 17
  • 30
1

Assuming there are two dictionaries with exact same keys, below is the most succinct way of doing it (python3 should be used for both the solution).


d1 = {'a': 1, 'b': 2, 'c':3}
d2 = {'a': 5, 'b': 6, 'c':7} 

# get keys from one of the dictionary
ks = [k for k in d1.keys()]

print(ks)
['a', 'b', 'c']

# call values from each dictionary on available keys
d_merged = {k: (d1[k], d2[k]) for k in ks}

print(d_merged)
{'a': (1, 5), 'b': (2, 6), 'c': (3, 7)}

# to merge values as list
d_merged = {k: [d1[k], d2[k]] for k in ks}
print(d_merged)
{'a': [1, 5], 'b': [2, 6], 'c': [3, 7]}

If there are two dictionaries with some common keys, but a few different keys, a list of all the keys should be prepared.


d1 = {'a': 1, 'b': 2, 'c':3, 'd': 9}
d2 = {'a': 5, 'b': 6, 'c':7, 'e': 4} 

# get keys from one of the dictionary
d1_ks = [k for k in d1.keys()]
d2_ks = [k for k in d2.keys()]

all_ks = set(d1_ks + d2_ks)

print(all_ks)
['a', 'b', 'c', 'd', 'e']

# call values from each dictionary on available keys
d_merged = {k: [d1.get(k), d2.get(k)] for k in all_ks}

print(d_merged)
{'d': [9, None], 'a': [1, 5], 'b': [2, 6], 'c': [3, 7], 'e': [None, 4]}

everestial007
  • 5,482
  • 7
  • 28
  • 59
0

To supplement the two-list solutions, here is a solution for processing a single list.

A sample list (NetworkX-related; manually formatted here for readability):

ec_num_list = [((src, tgt), ec_num['ec_num']) for src, tgt, ec_num in G.edges(data=True)]

print('\nec_num_list:\n{}'.format(ec_num_list))
ec_num_list:
[((82, 433), '1.1.1.1'),
  ((82, 433), '1.1.1.2'),
  ((22, 182), '1.1.1.27'),
  ((22, 3785), '1.2.4.1'),
  ((22, 36), '6.4.1.1'),
  ((145, 36), '1.1.1.37'),
  ((36, 154), '2.3.3.1'),
  ((36, 154), '2.3.3.8'),
  ((36, 72), '4.1.1.32'),
  ...] 

Note the duplicate values for the same edges (defined by the tuples). To collate those "values" to their corresponding "keys":

from collections import defaultdict
ec_num_collection = defaultdict(list)
for k, v in ec_num_list:
    ec_num_collection[k].append(v)

print('\nec_num_collection:\n{}'.format(ec_num_collection.items()))
ec_num_collection:
[((82, 433), ['1.1.1.1', '1.1.1.2']),   ## << grouped "values"
((22, 182), ['1.1.1.27']),
((22, 3785), ['1.2.4.1']),
((22, 36), ['6.4.1.1']),
((145, 36), ['1.1.1.37']),
((36, 154), ['2.3.3.1', '2.3.3.8']),    ## << grouped "values"
((36, 72), ['4.1.1.32']),
...] 

If needed, convert that list to dict:

ec_num_collection_dict = {k:v for k, v in zip(ec_num_collection, ec_num_collection)}

print('\nec_num_collection_dict:\n{}'.format(dict(ec_num_collection)))
  ec_num_collection_dict:
  {(82, 433): ['1.1.1.1', '1.1.1.2'],
  (22, 182): ['1.1.1.27'],
  (22, 3785): ['1.2.4.1'],
  (22, 36): ['6.4.1.1'],
  (145, 36): ['1.1.1.37'],
  (36, 154): ['2.3.3.1', '2.3.3.8'],
  (36, 72): ['4.1.1.32'],
  ...}

References

Victoria Stuart
  • 3,978
  • 2
  • 37
  • 31
0

From blubb answer:

You can also directly form the tuple using values from each list

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = (d1[k], d2[k])

This might be useful if you had a specific ordering for your tuples

ds = [d1, d2, d3, d4]
d = {}
for k in d1.keys():
  d[k] = (d3[k], d1[k], d4[k], d2[k]) #if you wanted tuple in order of d3, d1, d4, d2
frozen5032
  • 33
  • 6
0
def merge(d1, d2, merge):
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge(result[k], v)
        else:
            result[k] = v
    return result

d1 = {'a': 1, 'b': 2}
d2 = {'a': 1, 'b': 3, 'c': 2}
print merge(d1, d2, lambda x, y:(x,y))

{'a': (1, 1), 'c': 2, 'b': (2, 3)}
posdef
  • 6,366
  • 11
  • 43
  • 90
ralphtheninja
  • 117,827
  • 20
  • 105
  • 119
0

This library helped me, I had a dict list of nested keys with the same name but with different values, every other solution kept overriding those nested keys.

https://pypi.org/project/deepmerge/

from deepmerge import always_merger

def process_parms(args):
    temp_list = []
    for x in args:
        with open(x, 'r') as stream:
            temp_list.append(yaml.safe_load(stream))

    return always_merger.merge(*temp_list)
jmcgrath207
  • 895
  • 2
  • 14
  • 26
0

If keys are nested:

d1 = { 'key1': { 'nkey1': 'x1' }, 'key2': { 'nkey2': 'y1' } } 
d2 = { 'key1': { 'nkey1': 'x2' }, 'key2': { 'nkey2': 'y2' } }
ds = [d1, d2]
d = {}
for k in d1.keys():
    for k2 in d1[k].keys():
        d.setdefault(k, {})
        d[k].setdefault(k2, [])
        d[k][k2] = tuple(d[k][k2] for d in ds)

yields:

{'key1': {'nkey1': ('x1', 'x2')}, 'key2': {'nkey2': ('y1', 'y2')}}
lys
  • 702
  • 2
  • 8
  • 27
0

A better representation for two or more dicts with the same keys is a pandas Data Frame IMO:

d1 = {"key1": "x1", "key2": "y1"}  
d2 = {"key1": "x2", "key2": "y2"}  
d3 = {"key1": "x3", "key2": "y3"}  

d1_df = pd.DataFrame.from_dict(d1, orient='index')
d2_df = pd.DataFrame.from_dict(d2, orient='index')
d3_df = pd.DataFrame.from_dict(d3, orient='index')

fin_df = pd.concat([d1_df, d2_df, d3_df], axis=1).T.reset_index(drop=True)
fin_df

    key1 key2
0   x1   y1
1   x2   y2
2   x3   y3
mirekphd
  • 2,771
  • 21
  • 35
0

Using below method we can merge two dictionaries having same keys.

def update_dict(dict1: dict, dict2: dict) -> dict:
output_dict = {}
for key in dict1.keys():
    output_dict.update({key: []})
    if type(dict1[key]) != str:
        for value in dict1[key]:
            output_dict[key].append(value)
    else:
        output_dict[key].append(dict1[key])
    if type(dict2[key]) != str:
        for value in dict2[key]:
            output_dict[key].append(value)
    else:
        output_dict[key].append(dict2[key])

return output_dict

Input: d1 = {key1: x1, key2: y1} d2 = {key1: x2, key2: y2}
Output: {'key1': ['x1', 'x2'], 'key2': ['y1', 'y2']}

Amandeep Singh
  • 185
  • 1
  • 8
0

There is a great library funcy doing what you need in a just one, short line.

from funcy import join_with
from pprint import pprint

d1 = {"key1": "x1", "key2": "y1"}
d2 = {"key1": "x2", "key2": "y2"}

list_of_dicts = [d1, d2]

merged_dict = join_with(tuple, list_of_dicts)

pprint(merged_dict)

Output:

{'key1': ('x1', 'x2'), 'key2': ('y1', 'y2')}

More info here: funcy -> join_with.

Konrad
  • 115
  • 10
0

Modifying this answer to create a dictionary of tuples (what the OP asked for), instead of a dictionary of lists:

from collections import defaultdict

d1 = {1: 2, 3: 4}
d2 = {1: 6, 3: 7}

dd = defaultdict(tuple)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key] += (value,)

print(dd)

The above prints the following:

defaultdict(<class 'tuple'>, {1: (2, 6), 3: (4, 7)})
Sean McCarthy
  • 3,784
  • 7
  • 31
  • 47
0
dicts = [dict1,dict2,dict3]
out   = dict(zip(dicts[0].keys(),[[dic[list(dic.keys())[key]] for dic in dicts] for key in range(0,len(dicts[0]))]))
lemon
  • 2,990
  • 8
  • 28
Akshay
  • 56
  • 1
  • 9
  • While this may answer the question, it's very hard to read. Please include an explanation as to how and why this works. – joanis May 14 '22 at 17:50
  • 1
    We will **zip** dictionary Key's(dicts[0].keys() since all the dictionaries have same keys) and list containing values of each keys with the help of list comprehension( [[dic[list(dic.keys())[key]] ,this will select all the values of each key from each dictionary using for loops).Since **zip** functions output is tuple taking **dict** of it will create dictionary. – Akshay May 15 '22 at 16:53
-3

A compact possibility

d1={'a':1,'b':2}
d2={'c':3,'d':4}
context={**d1, **d2}
context
{'b': 2, 'c': 3, 'd': 4, 'a': 1}
user2897775
  • 729
  • 5
  • 8
  • the question is about merging dicts with same key. your is not the required answer. – Pbd Apr 24 '17 at 05:19
  • https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions explains the **XX – Fred Feb 23 '22 at 22:14