Python Dictionary Comprehension [duplicate]
Asked Answered
H

9

524

Is it possible to create a dictionary comprehension in Python (for the keys)?

Without list comprehensions, you can use something like this:

l = []
for n in range(1, 11):
    l.append(n)

We can shorten this to a list comprehension: l = [n for n in range(1, 11)].

However, say I want to set a dictionary's keys to the same value. I can do:

d = {}
for n in range(1, 11):
     d[n] = True # same value for each

I've tried this:

d = {}
d[i for i in range(1, 11)] = True

However, I get a SyntaxError on the for.

In addition (I don't need this part, but just wondering), can you set a dictionary's keys to a bunch of different values, like this:

d = {}
for n in range(1, 11):
    d[n] = n

Is this possible with a dictionary comprehension?

d = {}
d[i for i in range(1, 11)] = [x for x in range(1, 11)]

This also raises a SyntaxError on the for.

Hanoverian answered 24/1, 2013 at 17:51 Comment(1)
For future readers' info: NumPy arrays do let you set multiple elements to a single value or list of values, the way you're trying to do. Though if you don't already have a reason to use NumPy, it's probably not worth it just for this feature.Denson
M
686

There are dictionary comprehensions in Python 2.7+, but they don't work quite the way you're trying. Like a list comprehension, they create a new dictionary; you can't use them to add keys to an existing dictionary. Also, you have to specify the keys and values, although of course you can specify a dummy value if you like.

>>> d = {n: n**2 for n in range(5)}
>>> print d
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

If you want to set them all to True:

>>> d = {n: True for n in range(5)}
>>> print d
{0: True, 1: True, 2: True, 3: True, 4: True}

What you seem to be asking for is a way to set multiple keys at once on an existing dictionary. There's no direct shortcut for that. You can either loop like you already showed, or you could use a dictionary comprehension to create a new dict with the new values, and then do oldDict.update(newDict) to merge the new values into the old dict.

Mouthful answered 24/1, 2013 at 17:54 Comment(3)
FWIW, dict.update can also accept an iterable of key-value pairs just like the dict constructorBangalore
Note that if you to create a dictionary with all values the same, use dict.fromkeys(). So to set all values to True, use dict.fromkeys(range(5), True). Watch out, the value is not copied, so you may want to avoid this when you have a mutable value; it'll be shared between all keys.Gascon
Note: the keys can be the result of a method as well: { n*2 : n for n in range(3) } => {0: 0, 2: 1, 4: 2}. Both can be done in the same expression: { n*2 : n*3 for n in range(3) } => { 0: 0, 2: 3, 4: 6 }.Yah
B
171

You can use the dict.fromkeys class method ...

>>> dict.fromkeys(range(5), True)
{0: True, 1: True, 2: True, 3: True, 4: True}

This is the fastest way to create a dictionary where all the keys map to the same value.

But do not use this with mutable objects:

d = dict.fromkeys(range(5), [])
# {0: [], 1: [], 2: [], 3: [], 4: []}
d[1].append(2)
# {0: [2], 1: [2], 2: [2], 3: [2], 4: [2]} !!!

If you don't actually need to initialize all the keys, a defaultdict might be useful as well:

from collections import defaultdict
d = defaultdict(True)

To answer the second part, a dict-comprehension is just what you need:

{k: k for k in range(10)}

You probably shouldn't do this but you could also create a subclass of dict which works somewhat like a defaultdict if you override __missing__:

>>> class KeyDict(dict):
...    def __missing__(self, key):
...       #self[key] = key  # Maybe add this also?
...       return key
... 
>>> d = KeyDict()
>>> d[1]
1
>>> d[2]
2
>>> d[3]
3
>>> print(d)
{}
Bangalore answered 24/1, 2013 at 17:53 Comment(1)
Note that in the case of d = defaultdict(lambda: True), the lambda is not required as True is (or shouldn't) be mutable.Mcnabb
E
35
>>> {i:i for i in range(1, 11)}
{1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10}
Evictee answered 24/1, 2013 at 17:54 Comment(0)
K
33

I really like the @mgilson comment, since if you have a two iterables, one that corresponds to the keys and the other the values, you can also do the following.

keys = ['a', 'b', 'c']
values = [1, 2, 3]
d = dict(zip(keys, values))

giving

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

Kerwinn answered 16/8, 2014 at 18:3 Comment(0)
G
14

The main purpose of a list comprehension is to create a new list based on another one without changing or destroying the original list.

Instead of writing

l = []
for n in range(1, 11):
    l.append(n)

or

l = [n for n in range(1, 11)]

you should write only

l = range(1, 11)

In the two top code blocks you're creating a new list, iterating through it and just returning each element. It's just an expensive way of creating a list copy.

To get a new dictionary with all keys set to the same value based on another dict, do this:

old_dict = {'a': 1, 'c': 3, 'b': 2}
new_dict = { key:'your value here' for key in old_dict.keys()}

You're receiving a SyntaxError because when you write

d = {}
d[i for i in range(1, 11)] = True

you're basically saying: "Set my key 'i for i in range(1, 11)' to True" and "i for i in range(1, 11)" is not a valid key, it's just a syntax error. If dicts supported lists as keys, you would do something like

d[[i for i in range(1, 11)]] = True

and not

d[i for i in range(1, 11)] = True

but lists are not hashable, so you can't use them as dict keys.

Greenlaw answered 24/1, 2013 at 18:53 Comment(0)
N
14

Consider this example of counting the occurrence of words in a list using dictionary comprehension

my_list = ['hello', 'hi', 'hello', 'today', 'morning', 'again', 'hello']
my_dict = {k:my_list.count(k) for k in my_list}
print(my_dict)

And the result is

{'again': 1, 'hi': 1, 'hello': 3, 'today': 1, 'morning': 1}
Needful answered 17/2, 2016 at 19:51 Comment(3)
This is interesting, though not the most efficient as you'll be counting keys like 'hello' multiple timesKathlenekathlin
Keep in mind this is O(N^2) and incredibly slow even for small data sizes. I added it to some production code and this was a huge bottleneck tbh this answer is dangerous, be warned.Maragaret
I know that you are just showing this as an example of using a dictionary comprehension, but if you want to count the number of occurrences in a list, it would probably be better to use Counter from collections.Homily
G
13

Use dict() on a list of tuples, this solution will allow you to have arbitrary values in each list, so long as they are the same length

i_s = range(1, 11)
x_s = range(1, 11)
# x_s = range(11, 1, -1) # Also works
d = dict([(i_s[index], x_s[index], ) for index in range(len(i_s))])
Georg answered 24/1, 2013 at 17:55 Comment(1)
As a side note, this is the same thing as d = dict(zip(i_s,x_s))Bangalore
C
3

A dictionary comprehension is very much like a list comprehension, but we get a dictionary at the end of it, so we need to be assigning key value pairs instead of only values.

Let's say we've got a list of users, where each user information is stored in a tuple. So we have a list with four user tuples. Inside it, they have an ID, a unique identifying number for each user, a username, and a password.

So, we want to create a mapping of usernames to user information. This is something that you'll be doing very often, especially if you're doing something like web applications and things like that.

users = [
    (0, "Bob", "password"),
    (1, "code", "python"),
    (2, "Stack", "overflow"),
    (3, "username", "1234"),
]

username_mapping = {user[1]: user for user in users}
userid_mapping = {user[0]: user for user in users}

print(username_mapping)

"""
Why can this be helpful?

Well, imagine you know a user's username,and you want to get their information out.
You just access, let's say, "Bob," in your username_mapping, and you've got the information out.
"""
print(username_mapping["Bob"])  # (0, "Bob", "password")

# -- Can be useful to log in for example --

username_input = input("Enter your username: ")
password_input = input("Enter your password: ")

_, username, password = username_mapping[username_input]

if password_input == password:
    print("Your details are correct!")
else:
    print("Your details are incorrect.")

So this is an example of performing some sort of log-in using this structure here, this dictionary comprehension.

This is really helpful because it saves you from having to do another for loop here, to make sure that you are using the right username for their input.

Chromatolysis answered 14/3, 2021 at 20:21 Comment(0)
D
-2

you can't hash a list like that. try this instead, it uses tuples

d[tuple([i for i in range(1,11)])] = True
Daub answered 24/1, 2013 at 17:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.