10

Question

According to this answer, in Python 3.5 or greater, it is possible to merge two dictionaries x and y by unpacking them:

z = {**x, **y}

Is it possible to unpack a variadic list of dictionaries? Something like

def merge(*dicts):
    return {***dicts} # this fails, of course. What should I use here?

For instance, I would expect that

list_of_dicts = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]
{***list_of_dicts} == {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Note that this question is not about how to merge lists of dictionaries since the link above provides an answer to this. The question here is: is it possible, and how, to unpack lists of dictionaries?

Edit

As stated in the comments, this question is very similar to this one. However, unpacking a list of dictionaries is different from simply merging them. Supposing that there was an operator *** designed to unpack lists of dictionaries, and given

def print_values(a, b, c, d):
    print('a =', a)
    print('b =', b)
    print('c =', c)
    print('d =', d)

list_of_dicts = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]

it would be possible to write

print_values(***list_of_dicts)

instead of

print_values(**merge(list_of_dicts))
ruancomelli
  • 610
  • 8
  • 17
  • 1
    No, there is not a native way to do so with syntax sugar. – Maximilian Burszley Nov 19 '19 at 18:20
  • @TheIncorrigible1, I think that your comment is the most exact answer to this question. Thank you. – ruancomelli Nov 20 '19 at 11:43
  • Related, maybe even a duplicate since the answer to this question is simply "No": [How do I merge a list of dicts into a single dict?](https://stackoverflow.com/q/3494906/7851470) – Georgy Nov 21 '19 at 15:01
  • @Georgy, thanks for pointing that out. In fact, the solution `{k: v for d in L for k, v in d.items()}` seems really nice. I'm not sure, however, if this is a duplicate since I was looking for a syntax sugar for unpacking lists of dictionaries, while that question concerned merging. – ruancomelli Nov 21 '19 at 15:25
  • 1
    @Georgy, I edited the question aiming to show another application of unpacking besides merging. Hopefully, it clarifies the distinction between my question and the one shared by you. – ruancomelli Nov 21 '19 at 16:59

8 Answers8

6

Another solution is using collections.ChainMap

from collections import ChainMap

dict(ChainMap(*list_of_dicts[::-1]))

Out[88]: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Andy L.
  • 24,909
  • 4
  • 17
  • 29
  • 1
    ChainMap works even better than merging: it avoids creating a new dict. Also, it is possible to see ChainMap as a list of dicts (through ChainMap.maps) or as a merged dict (as you showed in your answer). Moreover, it provides more flexibility and uses a standard package instead of a self-implemented function. Very good, thank you! – ruancomelli Apr 06 '20 at 14:09
  • 1
    Additionally, it supports both types of unpacking. Defining `cm = ChainMap(*list_of_dicts[::-1])`, one can use list unpacking, as in `*cm.maps`, and dict unpacking, as in `**cm`. – ruancomelli Apr 06 '20 at 14:12
  • 2
    Came here to see why my answer had been unaccepted; this is much better. I don't think I ever noticed `ChainMap` before. – chepner Apr 07 '20 at 11:59
5

There's no syntax for that, but you can use itertools.chain to concatenate the key/value tuples from each dict into a single stream that dict can consume.

from itertools import chain


def merge(*dicts):
    return dict(chain.from_iterable(d.items() for d in dicts))

You can also unpack a list created by a list comprehension as well:

def merge(*dicts):
    return dict(*[d.items() for d in dicts])
chepner
  • 497,756
  • 71
  • 530
  • 681
5

You could just iterate over the list and use update:

lst = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]

dct = {}
for item in lst:
    dct.update(item)

print(dct)
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Jan
  • 42,290
  • 8
  • 54
  • 79
  • Thank you, this seems very good for merging `dict`s. Actually, it is the answer provided in [this post](https://stackoverflow.com/a/26853961/5811400). However, this focuses more on the merging instead of the use of unpacking, which was my first concern, as stated in the question. – ruancomelli Nov 20 '19 at 14:07
2

To merge multiple dictionaries you can use the function reduce:

from functools import reduce

lst = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]

reduce(lambda x, y: dict(**x, **y), lst)
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73
  • This one is indeed really nice. Just curious (this is not critical in my problem), regarding performance, is this implementation faster than the others? – ruancomelli Nov 20 '19 at 11:55
  • 1
    @rugortal I don't think so. In this solution your build a new dictionary for each subdictionary and in another solution with `update` you have one dictionary and update it. – Mykola Zotko Nov 20 '19 at 12:51
1

You could use list comprehension and put this iterable object as an argument to dict

def merge(*dicts):
    lst = [*[d.items() for d in dicts]]
    return dict(lst)
Jevs
  • 25
  • 5
  • Thanks for your answer. This is very close to what I was looking for. I'll stick to the selected answer for two reasons, though: it was posted before, and that version doesn't create the intermediate `lst`. – ruancomelli Nov 20 '19 at 14:01
  • Can you post the whole code because I can't get it to work. – Subham Mar 08 '21 at 06:59
0

You can just use a list comprehension to iterate over all the dicts in the list and then iterate over each if those dicts' items and finally convert them to dict

>>> lst = [{'a':1}, {'b':2}, {'c':1}, {'d':2}]
>>> dict(kv for d in lst for kv in d.items())
{'a': 1, 'b': 2, 'c': 1, 'd': 2}
Prem Anand
  • 2,469
  • 16
  • 16
0

You can use reduce to merge two dicts at a time using dict.update

>>> from functools import reduce
>>> lst = [{'a':1}, {'b':2}, {'c':1}, {'d':2}]
>>> reduce(lambda d1, d2: d1.update(d2) or d1, lst, {})
{'a': 1, 'b': 2, 'c': 1, 'd': 2}

Prem Anand
  • 2,469
  • 16
  • 16
0

When you *dicts its put in as a tuple, you can pull the list out with d[0], then use this comprehension for nonuniform keys

list_of_dicts = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]

def merge(*dicts):    
    return dict( j for i in dicts[0] for j in i.items())

print(merge(list_of_dicts)) 
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
[Program finished]
Subham
  • 397
  • 1
  • 6
  • 14