151

I have multiple dicts (or sequences of key-value pairs) like this:

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

How can I efficiently get a result like this, as a new dict?

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

See also: How can one make a dictionary with duplicate keys in Python?.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Salil
  • 9,534
  • 9
  • 42
  • 56

18 Answers18

127

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) # result: defaultdict(<type 'list'>, {1: [2, 6], 3: [4, 7]})
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • 1
    I think the OP wants the values as `tuple` not `list`. – user225312 May 10 '11 at 07:08
  • 6
    @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
65

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,510
  • 3
  • 40
  • 82
11

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',
    'd': 'donkey',
}
combine_dict(d1, d2)
# Returns: {
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',),
#    'd': ('donkey'),
# }
Flux
  • 9,805
  • 5
  • 46
  • 92
  • what if arguments will be same or it will be diffferents numbers of arguments? for example d1 = { 'a': [1,2,3], 'b': 2, } d2` = { 'b': 'boat', 'c': 'car', 'a': [1,3] } – KyluAce Aug 26 '22 at 12:57
  • @KyluAce In your case, `combine_dict(d1, d2)` would return `{'b': (2, 'boat'), 'c': ('car',), 'a': ([1, 2, 3], [1, 3])}`. – Flux Aug 26 '22 at 13:04
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
  • 1,090
  • 14
  • 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
  • 3
    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
4

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(list(d1.keys()) + list(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']}
sateesh
  • 27,947
  • 7
  • 36
  • 45
  • 1
    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

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
  • 16,274
  • 7
  • 29
  • 29
2

Using precomputed keys

def merge(dicts):
    # First, figure out which keys are present.
    keys = set().union(*dicts)
    # Build a dict with those keys, using a list comprehension to
    # pull the values from the source dicts.
    return {
        k: [d[k] for d in dicts if k in d]
        for k in keys
    }

This is essentially Flux's answer, generalized for a list of input dicts.

The set().union trick works by making a set union of the keys in all the source dictionaries. The union method on a set (we start with an empty one) can accept an arbitrary number of arguments, and make a union of each input with the original set; and it can accept other iterables (it does not require other sets for the arguments) - it will iterate over them and look for all unique elements. Since iterating over a dict yields its keys, they can be passed directly to the union method.

In the case where the keys of all inputs are known to be the same, this can be simplified: the keys can be hard-coded (or inferred from one of the inputs), and the if check in the list comprehension becomes unnecessary:

def merge(dicts):
    return {
        k: [d[k] for d in dicts]
        for k in dicts[0].keys()
    }

This is analogous to blubb's answer, but using a dict comprehension rather than an explicit loop to build the final result.

We could also try something like Mahdi Ghelichi's answer:

def merge(dicts):
    values = zip(*(d.values() for d in ds))
    return dict(zip(dicts[0].keys(), values))

This should work in Python 3.5 and below: dicts with identical keys will store them in the same order, during the same run of the program (if you run the program again, you may get a different ordering, but still a consistent one). In 3.6 and above, dictionaries preserve their insertion order (though they are only guaranteed to do so by the specification in 3.7 and above). Thus, input dicts could have the same keys in a different order, which would cause the first zip to combine the wrong values. We can work around this by "sorting" the input dicts (re-creating them with keys in a consistent order, like [{k:d[k] for k in dicts[0].keys()} for d in dicts]. (In older versions, this would be extra work with no net effect.) However, this adds complexity, and this double-zip approach really doesn't offer any advantages over the previous one using a dict comprehension.

Building the result explicitly, discovering keys on the fly

As in Eli Bendersky's answer, but as a function:

from collections import defaultdict

def merge(dicts):
    result = defaultdict(list)
    for d in dicts:
        for key, value in d.items():
            result[key].append(value)
    return result

This will produce a defaultdict, a subclass of dict defined by the standard library. The equivalent code using only built-in dicts might look like:

def merge(dicts):
    result = {}
    for d in dicts:
        for key, value in d.items():
            result.setdefault(key, []).append(value)
    return result

Using other container types besides lists

The precomputed-key approach will work fine to make tuples; replace the list comprehension [d[k] for d in dicts if k in d] with tuple(d[k] for d in dicts if k in d). This passes a generator expression to the tuple constructor. (There is no "tuple comprehension".)

Since tuples are immutable and don't have an append method, the explicit loop approach should be modified by replacing .append(value) with += (value,). However, this may perform poorly if there is a lot of key duplication, since it must create a new tuple each time. It might be better to produce lists first and then convert the final result with something like {k: tuple(v) for (k, v) in merged.items()}.

Similar modifications can be made to get sets (although there is a set comprehension, using {}), Numpy arrays etc. For example, we can generalize both approaches with a container type like so:

def merge(dicts, value_type=list):
    # First, figure out which keys are present.
    keys = set().union(*dicts)
    # Build a dict with those keys, using a list comprehension to
    # pull the values from the source dicts.
    return {
        k: value_type(d[k] for d in dicts if k in d)
        for k in keys
    }

and

from collections import defaultdict

def merge(dicts, value_type=list):
    # We stick with hard-coded `list` for the first part,
    # because even other mutable types will offer different interfaces.
    result = defaultdict(list)
    for d in dicts:
        for key, value in d.items():
            result[key].append(value)
    # This is redundant for the default case, of course.
    return {k:value_type(v) for (k, v) in result}

If the input values are already sequences

Rather than wrapping the values from the source in a new list, often people want to take inputs where the values are all already lists, and concatenate those lists in the output (or concatenate tuples or 1-dimensional Numpy arrays, combine sets, etc.).

This is still a trivial modification. For precomputed keys, use a nested list comprehension, ordered to get a flat result:

def merge(dicts):
    keys = set().union(*dicts)
    return {
        k: [v for d in dicts if k in d for v in d[k]]
        # Alternately:
        # k: [v for d in dicts for v in d.get(k, [])]
        for k in keys
    }

One might instead think of using sum to concatenate results from the original list comprehension. Don't do this - it will perform poorly when there are a lot of duplicate keys. The built-in sum isn't optimized for sequences (and will explicitly disallow "summing" strings) and will try to create a new list with each addition internally.

With the explicit loop approach, use .extend instead of .append:

from collections import defaultdict

def merge(dicts):
    result = defaultdict(list)
    for d in dicts:
        for key, value in d.items():
            result[key].extend(value)
    return result

The extend method of lists accepts any iterable, so this will work with inputs that have tuples for the values - of course, it still uses lists in the output; and of course, those can be converted back as shown previously.

If the inputs have one item each

A common version of this problem involves input dicts that each have a single key-value pair. Alternately, the input might be (key, value) tuples (or lists).

The above approaches will still work, of course. For tuple inputs, converting them to dicts first, like [{k:v} for (k, v) in tuples], allows for using the directly. Alternately, the explicit iteration approach can be modified to accept the tuples directly, like in Victoria Stuart's answer:

from collections import defaultdict

def merge(pairs):
    result = defaultdict(list)
    for key, value in pairs:
        result[key].extend(value)
    return result

(The code was simplified because there is no need to iterate over key-value pairs when there is only one of them and it has been provided directly.)

However, for these single-item cases it may work better to sort the values by key and then use itertools.groupby. In this case, it will be easier to work with the tuples. That looks like:

from itertools import groupby

def merge(tuples):
    grouped = groupby(tuples, key=lambda t: t[0])
    return {k: [kv[1] for kv in ts] for k, ts in grouped}

Here, t is used as a name for one of the tuples from the input. The grouped iterator will provide pairs of a "key" value k (the first element that was common to the tuples being grouped) and an iterator ts over the tuples in that group. Then we extract the values from the key-value pairs kv in the ts, make a list from those, and use that as the value for the k key in the resulting dict.

To merge one-item dicts this way, of course, convert them to tuples first. One simple way to do this, for a list of one-item dicts, is [next(iter(d.items())) for d in dicts].

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
0

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
  • 6,665
  • 7
  • 32
  • 72
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.

Soren V. Raben
  • 179
  • 1
  • 14
0

If you have pandas installed and all the keys in all the dictionaries are same then you can do it in one line:

import pandas as pd
d1 = {key1: x1, key2: y1}
d2 = {key1: x2, key2: y2}
new_dict = pd.DataFrame([d1,d2]).to_dict('list')
Kartikeya Sharma
  • 1,335
  • 1
  • 10
  • 22
-1
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,498
  • 11
  • 46
  • 94
rtn
  • 127,556
  • 20
  • 111
  • 121
  • While people commonly ask to leave single values alone and only add tuple or list wrapping when there is more than one value, it is a bad idea to special-case like this. Generally, the subsequent code that processes the result will need *its own similar special-casing code* in order to handle the non-tuple values. The straightforward approach will avoid that complication. Simple is better than complex. Special cases aren't special enough to break the rules. – Karl Knechtel Feb 12 '23 at 14:27
-1

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
  • 949
  • 2
  • 9
  • 33
-1

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
  • 4,838
  • 8
  • 39
  • 61
-1
d1 ={'B': 10, 'C ': 7, 'A': 20}
d2 ={'B': 101, 'Y ': 7, 'X': 8}
d3 ={'A': 201, 'Y ': 77, 'Z': 8}

def CreateNewDictionaryAssemblingAllValues1(d1,d2,d3):
    aa = {
        k :[d[k] for d in (d1,d2,d3) if k in d ] for k in set(d1.keys() | d2.keys() | d3.keys()  )
        }
    aap = print(aa)
    
    return aap

CreateNewDictionaryAssemblingAllValues1(d1, d2, d3)

"""
Output :

{'X': [8], 'C ': [7], 'Y ': [7, 77], 'Z': [8], 'B': [10, 101], 'A': [20, 201]}

"""
Soudipta Dutta
  • 1,353
  • 1
  • 12
  • 7
-2

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
  • 29
  • 7
-2

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
  • 320
  • 2
  • 9
-2
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
  • 14,875
  • 6
  • 18
  • 38
Akshay
  • 52
  • 1
  • 9
  • 1
    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
  • This is (as far as I can tell) effectively the same as Mahdi Ghelichi's approach, just written more compactly and obscurely. – Karl Knechtel Feb 12 '23 at 14:41
-5

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
  • 725
  • 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