Python Dictionary Comprehensions
Python Dictionary Comprehension Tutorial
You’ll learn:
- What it is, why it is important and how it can serve as an alternative to for loops and lambda functions.
- How to add conditionals into dictionary comprehensions: you will work with if conditions, multiple if conditions and also if-else statements.
- What nested dictionary comprehension is, how you can use it and how you can potentially rewrite it with for loops.
Dictionary comprehension is a method for transforming one dictionary into another dictionary. During this transformation, items within the original dictionary can be conditionally included in the new dictionary and each item can be transformed as needed.
A good list comprehension can make your code more expressive and thus, easier to read. The key with creating comprehensions is to not let them get so complex that your head spins when you try to decipher what they are actually doing. Keeping the idea of “easy to read” alive.
The way to do dictionary comprehension in Python is to be able to access the key objects and the value objects of a dictionary.
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# Put all keys of `dict1` in a list and returns the list
keys = dict1.keys()
print(type(keys), keys)
<class 'dict_keys'> dict_keys(['a', 'b', 'c', 'd'])
# Put all values saved in `dict1` in a list and returns the list
vals = dict1.values()
print(type(vals), vals)
<class 'dict_values'> dict_values([1, 2, 3, 4])
z = { 'x': 1, 'y': None, None: 'a'}
print(z)
print(type(None))
{'x': 1, 'y': None, None: 'a'}
<class 'NoneType'>
So, now that you know how to access all the keys and their values in a dictionary. You can also access each key-value pair within a dictionary using the items() method:
items = dict1.items()
print(type(items), items)
<class 'dict_items'> dict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
This is the general template you can follow for dictionary comprehension. This can serve as the basic and the most simple template which can get more and more complex as you add conditionalities to it.
dict_variable = {key:value for (key,value) in dictonary.items()}
# Double each value in the dictionary
double_dict1 = {}
for (k,v) in dict1.items():
double_dict1[k] = v*2
print(double_dict1)
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
# Double each value in the dictionary
double_dict1 = {k:v*2 for (k,v) in dict1.items()}
print(double_dict1)
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
# Double each value in the dictionary
double_dict1 = {x[0]:x[1]*2 for x in dict1.items()}
print(double_dict1)
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
double_dict1 = {k:dict1[k]*2 for k in dict1.keys()}
print(double_dict1)
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
You can also make changes to the key values. For example, let’s create the same dictionary as above but also change the names of the key.
dict1_keys = {k*4:v for (k,v) in dict1.items()}
print(dict1_keys)
{'aaaa': 1, 'bbbb': 2, 'cccc': 3, 'dddd': 4, 'eeee': 5}
Why Use Dictionary Comprehension?
Dictionary comprehension is a powerful concept and can be used to substitute for loops and lambda functions. However, not all for loop can be written as a dictionary comprehension but all dictionary comprehension can be written with a for loop.
Consider the following problem, where you want to create a new dictionary where the key is a number divisible by 2 in a range of 0-10 and it’s value is the square of the number.
Alternative to for loops
For loops are used to repeat a certain operation or a block of instructions in a program for a given number of times. However, nested for loops (for loop inside another for loop) can get confusing and complex. Dictionary comprehensions are better in such situations and can simplify the readability and your understanding of the code.
Let’s see how the same probem can be solved using a for loop and dictionary comprehension:
numbers = range(10)
new_dict_for = {}
# Add values to `new_dict` using for loop
for n in numbers:
if n % 2 == 0:
new_dict_for[n] = n**2
print(new_dict_for)
{0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# Use dictionary comprehension
new_dict_comp = {n:n**2 for n in numbers if n % 2 == 0}
print(new_dict_comp)
{0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
Alternative to lambda functions
Lambda functions are a way of creating small anonymous functions. They are functions without a name. These functions are throw-away functions, which are only needed where they have been created. Lambda functions are mainly used in combination with the functions filter()
, map()
and reduce()
.
Let’s look at the lambda function along with the map()
function:
# Initialize `fahrenheit` dictionary
fahrenheit = {'t1':-30, 't2':-20, 't3':-10, 't4':0}
#Get the corresponding `celsius` values
celsius = map(lambda x: (float(5)/9)*(x-32), fahrenheit.values())
celsius = list(celsius)
#Create the `celsius` dictionary
celsius_dict = dict(zip(fahrenheit.keys(), celsius))
print(celsius_dict)
{'t1': -34.44444444444444, 't2': -28.88888888888889, 't3': -23.333333333333336, 't4': -17.77777777777778}
Now, let’s try to solve the same problem using dictionary comprehension:
# Initialize the `fahrenheit` dictionary
fahrenheit = {'t1': -30,'t2': -20,'t3': -10,'t4': 0}
# Get the corresponding `celsius` values and create the new dictionary
celsius = {k:(float(5)/9)*(v-32) for (k,v) in fahrenheit.items()}
print(celsius_dict)
{'t1': -34.44444444444444, 't2': -28.88888888888889, 't3': -23.333333333333336, 't4': -17.77777777777778}
Adding Conditionals to Dictionary Comprehension
You often need to add conditions to a solution while tackling problems. Let’s explore how you can add conditionals into dictionary comprehension to make it more powerful.
If Condition
Let’s suppose you need to create a new dictionary from a given dictionary but with items that are greater than 2. This means that you need to add a condition to the original template you saw above…
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# Check for items greater than 2
dict1_cond = {k:v for (k,v) in dict1.items() if v > 2}
print(dict1_cond)
{'c': 3, 'd': 4, 'e': 5}
Multiple If Conditions
In the problem above, what if you have to not only get the items greater than 2 but also need to check if they are multiples of 2 at the same time.
dict1_doubleCond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0}
print(dict1_doubleCond)
{'d': 4}
dict1_doubleCond = {k:v for (k,v) in dict1.items() if v>2 and v%2 == 0}
print(dict1_doubleCond)
{'d': 4}
dict1_doubleCond = {k:v for (k,v) in dict1.items() if v>2 or v%2 == 0}
print(dict1_doubleCond)
{'b': 2, 'c': 3, 'd': 4, 'e': 5}
The solution to adding multiple conditionals is as easy as simply adding the conditions one after another in your comprehension. However, you need to be careful about what you are trying to do in the problem. Remember, that the consecutive if statements work as if they had and
clauses between them.
Lets see one more example with three conditionals:
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6}
dict1_tripleCond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0 if v%3 == 0}
print(dict1_tripleCond)
{'f': 6}
In a for loop, this will correspond to:
dict1_tripleCond = {}
for (k,v) in dict1.items():
if (v>=2 and v%2 == 0 and v%3 == 0):
dict1_tripleCond[k] = v
print(dict1_tripleCond)
{'f': 6}
dict1_tripleCond = {}
for (k,v) in dict1.items():
if v>=2:
if v%2 == 0:
if v%3 == 0:
dict1_tripleCond[k] = v
print(dict1_tripleCond)
{'f': 6}
If-Else Conditions
Dealing with an if-else condition is also easy with dictionary comprehension. Check out the following example to see it for yourself:
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6}
# Identify odd and even entries
dict1_tripleCond = {k:('even' if v%2==0 else 'odd') for (k,v) in dict1.items()}
print(dict1_tripleCond)
{'a': 'odd', 'b': 'even', 'c': 'odd', 'd': 'even', 'e': 'odd', 'f': 'even'}
Nested Dictionary Comprehension
Nesting is a programming concept where data is organized in layers, or where objects contain other similar objects. You must have often seen a nested ‘if’ structure, which is an if condition inside another if condition.
Similarly, dictionaries can be nested and thus their comprehensions can be nested as well. Let’s see what this means:
nested_dict = {'first':{'a':1}, 'second':{'b':2}}
float_dict = { outer_k: {inner_k:float(inner_v) for (inner_k, inner_v) in outer_v.items()}
for (outer_k, outer_v) in nested_dict.items() }
print(float_dict)
{'first': {'a': 1.0}, 'second': {'b': 2.0}}
This is an example of a nested dictionary. The nested_dict
is a dictionary with the keys: first
and second
, which hold dictionary objects in their values. The code works with the inner dictionary values and converts them to float and then combines the outer keys with the new float inner values into a new dictionary.
The code also has a nested dictionary comprehension, which is dictionary comprehension inside another one. The dictionary comprehension when nested as you can see can get pretty hard to read as well as understand, which takes away the whole point of using comprehensions in the first place. As the structure of the dictionary you are working with gets complicated, the dictionary comprehension starts to get complicated as well. For such situations, you might be better off not using complicated comprehensions in your code.
Note that you can rewrite the above code chunk also with a nested for loop:
nested_dict = {'first':{'a':1}, 'second':{'b':2}}
for (outer_k, outer_v) in nested_dict.items():
for (inner_k, inner_v) in outer_v.items():
outer_v.update({inner_k: float(inner_v)})
nested_dict.update({outer_k:outer_v})
print(nested_dict)
{'first': {'a': 1.0}, 'second': {'b': 2.0}}