1744

I want to combine these:

keys = ['name', 'age', 'food']
values = ['Monty', 42, 'spam']

into a single dictionary:

{'name': 'Monty', 'age': 42, 'food': 'spam'}

How can I do this?

0

22 Answers 22

2916

Like this:

keys = ['a', 'b', 'c']
values = [1, 2, 3]
dictionary = dict(zip(keys, values))
print(dictionary) # {'a': 1, 'b': 2, 'c': 3}

Voila :-) The pairwise dict constructor and zip function are awesomely useful.

11
  • 29
    It is worth noting that dictionary = {zip(keys, values)} won't work. You have to explicitly declare as dict(...) Aug 28, 2019 at 15:52
  • 30
    Not sure why you'd expect it to, @FernandoWittmann. {thing} is syntactic sugar to construct a set() containing one element. {*iterable} is syntactic sugar to construct a set containing several elements. {k:v} or {**mapping} will construct a dict, but that's syntactically quite distinct.
    – Dan Lenski
    Aug 28, 2019 at 16:41
  • 15
    Thanks for the comment Dan. You are right. My confusion happened because I usually use the sintax {} for dictionaries. In fact, if we try type({}) the output is dict. But indeed, if we try type({thing}) then the output is set. Aug 28, 2019 at 17:42
  • 21
    I came here in case we can do better than {k:v for k, v in zip(keys, values)}. It turns out we can. +1.
    – J.G.
    Jan 23, 2020 at 14:28
  • 4
    This answer's revisions is being discussed at meta. Jun 1, 2022 at 22:52
240

Imagine that you have:

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')

What is the simplest way to produce the following dictionary ?

dict = {'name' : 'Monty', 'age' : 42, 'food' : 'spam'}

Most performant, dict constructor with zip

new_dict = dict(zip(keys, values))

In Python 3, zip now returns a lazy iterator, and this is now the most performant approach.

dict(zip(keys, values)) does require the one-time global lookup each for dict and zip, but it doesn't form any unnecessary intermediate data-structures or have to deal with local lookups in function application.

Runner-up, dict comprehension:

A close runner-up to using the dict constructor is to use the native syntax of a dict comprehension (not a list comprehension, as others have mistakenly put it):

new_dict = {k: v for k, v in zip(keys, values)}

Choose this when you need to map or filter based on the keys or value.

In Python 2, zip returns a list, to avoid creating an unnecessary list, use izip instead (aliased to zip can reduce code changes when you move to Python 3).

from itertools import izip as zip

So that is still (2.7):

new_dict = {k: v for k, v in zip(keys, values)}

Python 2, ideal for <= 2.6

izip from itertools becomes zip in Python 3. izip is better than zip for Python 2 (because it avoids the unnecessary list creation), and ideal for 2.6 or below:

from itertools import izip
new_dict = dict(izip(keys, values))

Result for all cases:

In all cases:

>>> new_dict
{'age': 42, 'name': 'Monty', 'food': 'spam'}

Explanation:

If we look at the help on dict we see that it takes a variety of forms of arguments:


>>> help(dict)

class dict(object)
 |  dict() -> new empty dictionary
 |  dict(mapping) -> new dictionary initialized from a mapping object's
 |      (key, value) pairs
 |  dict(iterable) -> new dictionary initialized as if via:
 |      d = {}
 |      for k, v in iterable:
 |          d[k] = v
 |  dict(**kwargs) -> new dictionary initialized with the name=value pairs
 |      in the keyword argument list.  For example:  dict(one=1, two=2)

The optimal approach is to use an iterable while avoiding creating unnecessary data structures. In Python 2, zip creates an unnecessary list:

>>> zip(keys, values)
[('name', 'Monty'), ('age', 42), ('food', 'spam')]

In Python 3, the equivalent would be:

>>> list(zip(keys, values))
[('name', 'Monty'), ('age', 42), ('food', 'spam')]

and Python 3's zip merely creates an iterable object:

>>> zip(keys, values)
<zip object at 0x7f0e2ad029c8>

Since we want to avoid creating unnecessary data structures, we usually want to avoid Python 2's zip (since it creates an unnecessary list).

Less performant alternatives:

This is a generator expression being passed to the dict constructor:

generator_expression = ((k, v) for k, v in zip(keys, values))
dict(generator_expression)

or equivalently:

dict((k, v) for k, v in zip(keys, values))

And this is a list comprehension being passed to the dict constructor:

dict([(k, v) for k, v in zip(keys, values)])

In the first two cases, an extra layer of non-operative (thus unnecessary) computation is placed over the zip iterable, and in the case of the list comprehension, an extra list is unnecessarily created. I would expect all of them to be less performant, and certainly not more-so.

Performance review:

In 64 bit Python 3.8.2 provided by Nix, on Ubuntu 16.04, ordered from fastest to slowest:

>>> min(timeit.repeat(lambda: dict(zip(keys, values))))
0.6695233230129816
>>> min(timeit.repeat(lambda: {k: v for k, v in zip(keys, values)}))
0.6941362579818815
>>> min(timeit.repeat(lambda: {keys[i]: values[i] for i in range(len(keys))}))
0.8782548159942962
>>> 
>>> min(timeit.repeat(lambda: dict([(k, v) for k, v in zip(keys, values)])))
1.077607496001292
>>> min(timeit.repeat(lambda: dict((k, v) for k, v in zip(keys, values))))
1.1840861019445583

dict(zip(keys, values)) wins even with small sets of keys and values, but for larger sets, the differences in performance will become greater.

A commenter said:

min seems like a bad way to compare performance. Surely mean and/or max would be much more useful indicators for real usage.

We use min because these algorithms are deterministic. We want to know the performance of the algorithms under the best conditions possible.

If the operating system hangs for any reason, it has nothing to do with what we're trying to compare, so we need to exclude those kinds of results from our analysis.

If we used mean, those kinds of events would skew our results greatly, and if we used max we will only get the most extreme result - the one most likely affected by such an event.

A commenter also says:

In python 3.6.8, using mean values, the dict comprehension is indeed still faster, by about 30% for these small lists. For larger lists (10k random numbers), the dict call is about 10% faster.

I presume we mean dict(zip(... with 10k random numbers. That does sound like a fairly unusual use case. It does makes sense that the most direct calls would dominate in large datasets, and I wouldn't be surprised if OS hangs are dominating given how long it would take to run that test, further skewing your numbers. And if you use mean or max I would consider your results meaningless.

Let's use a more realistic size on our top examples:

import numpy
import timeit
l1 = list(numpy.random.random(100))
l2 = list(numpy.random.random(100))

And we see here that dict(zip(... does indeed run faster for larger datasets by about 20%.

>>> min(timeit.repeat(lambda: {k: v for k, v in zip(l1, l2)}))
9.698965263989521
>>> min(timeit.repeat(lambda: dict(zip(l1, l2))))
7.9965161079890095
3
  • 2
    As of mid-2019 (python 3.7.3), I find different timings. %%timeit returns 1.57 \pm 0.019microsec for dict(zip(headList, textList)) & 1.95 \pm 0.030 microsec for {k: v for k, v in zip(headList, textList)}. I would suggest the former for readability and speed. Obviously this gets at the min() vs mean() argument for timeit. Jul 2, 2019 at 15:06
  • It seems that you're saying the dict comprehension is fastest, but then in the performance review, dict(zip(keys, values)) looks faster. Maybe you forgot to update something? Apr 8, 2020 at 2:57
  • 1
    Minor note (largely irrelevant given Python 2 EOL): You can use from future_builtins import zip as an alternative to from itertools import izip as zip that's slightly more explicit about describing the import in terms of getting the Python 3 zip as a replacement for regular zip. It's exactly equivalent to be clear (future_builtins.zip is itself just an alias of itertools.izip). Oct 29, 2020 at 18:24
138

Try this:

>>> import itertools
>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> adict = dict(itertools.izip(keys,values))
>>> adict
{'food': 'spam', 'age': 42, 'name': 'Monty'}

In Python 2, it's also more economical in memory consumption compared to zip.

2
41
keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')
out = dict(zip(keys, values))

Output:

{'food': 'spam', 'age': 42, 'name': 'Monty'}
31

You can also use dictionary comprehensions in Python ≥ 2.7:

>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> {k: v for k, v in zip(keys, values)}
{'food': 'spam', 'age': 42, 'name': 'Monty'}
0
21

A more natural way is to use dictionary comprehension

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}
1
  • sometime it's the fastest way and sometime it's slowest to convert to dict object, why is it so?, thanks dude. Aug 8, 2019 at 8:24
16

If you need to transform keys or values before creating a dictionary then a generator expression could be used. Example:

>>> adict = dict((str(k), v) for k, v in zip(['a', 1, 'b'], [2, 'c', 3])) 

Take a look Code Like a Pythonista: Idiomatic Python.

0
15

with Python 3.x, goes for dict comprehensions

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')

dic = {k:v for k,v in zip(keys, values)}

print(dic)

More on dict comprehensions here, an example is there:

>>> print {i : chr(65+i) for i in range(4)}
    {0 : 'A', 1 : 'B', 2 : 'C', 3 : 'D'}
0
11

For those who need simple code and aren’t familiar with zip:

List1 = ['This', 'is', 'a', 'list']
List2 = ['Put', 'this', 'into', 'dictionary']

This can be done by one line of code:

d = {List1[n]: List2[n] for n in range(len(List1))}
3
  • 7
    fails loudly if List1 is longer than List2 Sep 13, 2017 at 11:49
  • @Jean-FrançoisFabre Does it reallly matter? what is the reason that we should ǵive two lists with different length to build a dictionary? Dec 17, 2019 at 22:35
  • 1
    probably not, but after this for n in range(len(List1)) is an anti-pattern Dec 18, 2019 at 12:41
7

Here is also an example of adding a list value in your dictionary:

listKey = ["Name", "Surname", "Age"]
listValue = [["Cyd", "Nikki", "Cindy"], ["Surname1", "Surname2", "Surname3"], [21, 32, 47]]
dic = dict(zip(listKey , listValue ))
print(dic)

always make sure the your "Key"(listKey) is always in the first parameter of dict.

For single value in your dictionary

singleKey = ["Name", "Surname", "Age"]
singleValue = ["Cyd"], ["Surname1"], [21]
dic = dict(zip(singleKey , singleValue ))
print(dic)

always make sure the your "Key"(singleKey ) is always in the first parameter of dict.

The output below of list

{
'Name': ['Cyd', 'Nikki', 'Cindy'], 
'Surname': ['Surname1', 'Surname2', 'Surname3'], 
'Age': [21, 32, 47]
}

Outpur of single value

{
'Name': ['Cyd'], 
'Surname': ['Surname1'], 
'Age': [21]
}
4
  • 2018-04-18

The best solution is still:

In [92]: keys = ('name', 'age', 'food')
...: values = ('Monty', 42, 'spam')
...: 

In [93]: dt = dict(zip(keys, values))
In [94]: dt
Out[94]: {'age': 42, 'food': 'spam', 'name': 'Monty'}

Tranpose it:

    lst = [('name', 'Monty'), ('age', 42), ('food', 'spam')]
    keys, values = zip(*lst)
    In [101]: keys
    Out[101]: ('name', 'age', 'food')
    In [102]: values
    Out[102]: ('Monty', 42, 'spam')
4

I had this doubt while I was trying to solve a graph-related problem. The issue I had was I needed to define an empty adjacency list and wanted to initialize all the nodes with an empty list, that's when I thought how about I check if it is fast enough, I mean if it will be worth doing a zip operation rather than simple assignment key-value pair. After all most of the times, the time factor is an important ice breaker. So I performed timeit operation for both approaches.

import timeit
def dictionary_creation(n_nodes):
    dummy_dict = dict()
    for node in range(n_nodes):
        dummy_dict[node] = []
    return dummy_dict


def dictionary_creation_1(n_nodes):
    keys = list(range(n_nodes))
    values = [[] for i in range(n_nodes)]
    graph = dict(zip(keys, values))
    return graph


def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

iteration = wrapper(dictionary_creation, n_nodes)
shorthand = wrapper(dictionary_creation_1, n_nodes)

for trail in range(1, 8):
    print(f'Itertion: {timeit.timeit(iteration, number=trails)}\nShorthand: {timeit.timeit(shorthand, number=trails)}')

For n_nodes = 10,000,000 I get,

Iteration: 2.825081646999024 Shorthand: 3.535717916001886

Iteration: 5.051560923002398 Shorthand: 6.255070794999483

Iteration: 6.52859034499852 Shorthand: 8.221581164998497

Iteration: 8.683652416999394 Shorthand: 12.599181543999293

Iteration: 11.587241565001023 Shorthand: 15.27298851100204

Iteration: 14.816342867001367 Shorthand: 17.162912737003353

Iteration: 16.645022411001264 Shorthand: 19.976680120998935

You can clearly see after a certain point, iteration approach at n_th step overtakes the time taken by shorthand approach at n-1_th step.

3

you can use this below code:

dict(zip(['name', 'age', 'food'], ['Monty', 42, 'spam']))

But make sure that length of the lists will be same.if length is not same.then zip function turncate the longer one.

1

If you are working with more than 1 set of values and wish to have a list of dicts you can use this:

def as_dict_list(data: list, columns: list):
    return [dict((zip(columns, row))) for row in data]

Real-life example would be a list of tuples from a db query paired to a tuple of columns from the same query. Other answers only provided for 1 to 1.

2
  • Is there any reason for the double brackets around the zip() call?
    – Woodman
    Dec 6, 2022 at 3:53
  • @Woodman good question. I don't know. From looking at the code here, it appears excessive.
    – DonkeyKong
    Dec 19, 2022 at 20:52
1

It can be done by the following way.

keys = ['name', 'age', 'food']
values = ['Monty', 42, 'spam'] 

dict = {}

for i in range(len(keys)):
    dict[keys[i]] = values[i]
    
print(dict)

{'name': 'Monty', 'age': 42, 'food': 'spam'}
1

All answers sum up:

l = [1, 5, 8, 9]
ll = [3, 7, 10, 11]

zip:

dict(zip(l,ll)) # {1: 3, 5: 7, 8: 10, 9: 11}

#if you want to play with key or value @recommended

{k:v*10 for k, v in zip(l, ll)} #{1: 30, 5: 70, 8: 100, 9: 110}

counter:

d = {}
c=0
for k in l:
    d[k] = ll[c] #setting up keys from the second list values
    c += 1
print(d)
{1: 3, 5: 7, 8: 10, 9: 11}

enumerate:

d = {}
for i,k in enumerate(l):
    d[k] = ll[i]
print(d)
{1: 3, 5: 7, 8: 10, 9: 11}
1

Most of the answers here mention zip() to create tuples with an item from each of keys and values lists. This works fine as long as the lists have the same length. If the lists have different lengths, it stops at the shorter list; thus ignoring the remaining elements in the longer list. The following example illustrates this point:

keys = ['name', 'age', 'food']
values = ['Monty', 42]
dict(zip(keys, values))                    # {'name': 'Monty', 'age': 42}

In cases where this behavior is not desirable, an analogous method itertools.zip_longest exists in the standard library which creates an iterator that iterates until the longer list is exhausted. By default, the missing values are filled with None.

from itertools import zip_longest
dict(zip_longest(keys, values))            # {'name': 'Monty', 'age': 42, 'food': None}

It also accepts fillvalue= kwarg, which can be used to specify what to fill in for the missing values.

dict(zip_longest(keys, values, fillvalue='spam')) # {'name': 'Monty', 'age': 42, 'food': 'spam'}

Note that if both lists have the same length, zip() and itertools.zip_longest() produce the same output:

keys = values = range(5)
dict(zip(keys, values)) == dict(zip_longest(keys, values))                       # True
{k:v for k,v in zip(keys, values)} == {k:v for k,v in zip_longest(keys, values)} # True
0

Solution as dictionary comprehension with enumerate:

dict = {item : values[index] for index, item in enumerate(keys)}

Solution as for loop with enumerate:

dict = {}
for index, item in enumerate(keys):
    dict[item] = values[index]
0
keys = ['name', 'age', 'food']
values = ['Monty', 42, 'spam']
dic = {}
c = 0
for i in keys:
    dic[i] = values[c]
    c += 1

print(dic)
{'name': 'Monty', 'age': 42, 'food': 'spam'}
0
import pprint

p = ['A', 'B', 'C']
q = [5, 2, 7]
r = ["M", "F", "M"]
s = ['Sovabazaar','Shyambazaar','Bagbazaar','Hatkhola']


def makeDictUsingAlternateLists1(**rest):
    print("*rest.keys() : ",*rest.keys())
    print("rest.keys() : ",rest.keys())
    print("*rest.values() : ",*rest.values())
    print("**rest.keys() : ",rest.keys())
    print("**rest.values() : ",rest.values())
    [print(a) for a in zip(*rest.values())]
    
    [ print(dict(zip(rest.keys(),a))) for a in zip(*rest.values())]
    print("...")
    
    
    finalRes= [ dict( zip( rest.keys(),a))  for a in zip(*rest.values())] 
    return finalRes
    
l = makeDictUsingAlternateLists1(p=p,q=q,r=r,s=s)
pprint.pprint(l)    
"""
*rest.keys() :  p q r s
rest.keys() :  dict_keys(['p', 'q', 'r', 's'])
*rest.values() :  ['A', 'B', 'C'] [5, 2, 7] ['M', 'F', 'M'] ['Sovabazaar', 'Shyambazaar', 'Bagbazaar', 'Hatkhola']
**rest.keys() :  dict_keys(['p', 'q', 'r', 's'])
**rest.values() :  dict_values([['A', 'B', 'C'], [5, 2, 7], ['M', 'F', 'M'], ['Sovabazaar', 'Shyambazaar', 'Bagbazaar', 'Hatkhola']])
('A', 5, 'M', 'Sovabazaar')
('B', 2, 'F', 'Shyambazaar')
('C', 7, 'M', 'Bagbazaar')
{'p': 'A', 'q': 5, 'r': 'M', 's': 'Sovabazaar'}
{'p': 'B', 'q': 2, 'r': 'F', 's': 'Shyambazaar'}
{'p': 'C', 'q': 7, 'r': 'M', 's': 'Bagbazaar'}
...
[{'p': 'A', 'q': 5, 'r': 'M', 's': 'Sovabazaar'},
 {'p': 'B', 'q': 2, 'r': 'F', 's': 'Shyambazaar'},
 {'p': 'C', 'q': 7, 'r': 'M', 's': 'Bagbazaar'}]
 
"""
-1

method without zip function

l1 = [1,2,3,4,5]
l2 = ['a','b','c','d','e']
d1 = {}
for l1_ in l1:
    for l2_ in l2:
        d1[l1_] = l2_
        l2.remove(l2_)
        break  

print (d1)


{1: 'd', 2: 'b', 3: 'e', 4: 'a', 5: 'c'}
1
  • Hi xiyurui, The input(l1 and l2) should be a list. If you assign l1 and l2 as a set it may not preserve insertion order. for me i got the output as {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
    – Noordeen
    Jan 31, 2019 at 8:32
-1

Although there are multiple ways of doing this but i think most fundamental way of approaching it; creating a loop and dictionary and store values into that dictionary. In the recursive approach the idea is still same it but instead of using a loop, the function called itself until it reaches to the end. Of course there are other approaches like using dict(zip(key, value)) and etc. These aren't the most effective solutions.

y = [1,2,3,4]
x = ["a","b","c","d"]

# This below is a brute force method
obj = {}
for i in range(len(y)):
    obj[y[i]] = x[i]
print(obj)

# Recursive approach 
obj = {}
def map_two_lists(a,b,j=0):
    if j < len(a):
        obj[b[j]] = a[j]
        j +=1
        map_two_lists(a, b, j)
        return obj
      


res = map_two_lists(x,y)
print(res)

Both the results should print

{1: 'a', 2: 'b', 3: 'c', 4: 'd'}  
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.