Dictionary is a powerful tool for storing and retrieving data efficiently.
In this blog post, we’ll explore the dictionary data structure in Python, its syntax, and its usage.
Python dictionary is implemented as a hashtable. A hashtable is a data structure that allows for fast lookups and inserts by mapping keys to values using a hash function.
When a key is added to a dictionary, its hash value is calculated using a hash function, and the key-value pair is stored at the index in the underlying array that corresponds to the hash value.
When you access a key in a dictionary, Python calculates the hash value of the key again and looks up the key in the corresponding index in the array. This makes dictionary lookups very fast, even for large dictionaries.
Hash tables are commonly used in many programming languages, including Python, for implementing dictionaries and sets because of their efficiency in searching, inserting, and deleting elements.
At a high level, a dictionary is an unordered collection of key-value pairs. The keys in a dictionary are unique and immutable, while the values can be of any type and mutable. You can think of a dictionary as a real-world dictionary where the words are the keys and the definitions are the values.
Let’s start with the syntax of a dictionary in Python. To create a dictionary, you use curly braces {}
and separate the key-value pairs with a colon :
. Here’s an example of a simple dictionary that maps names to ages:
my_dict = {'Sachin': 25, 'Virat': 20, 'Ramesh': 35}
In this example, the keys are strings (‘Sachin’, ‘Virat’, and ‘Ramesh’), and the values are integers (25, 20, and 35). You can access the values in a dictionary by using the keys as the index:
print(my_dict['Sachin']) # Output: 25
If you try to access a key that does not exist in the dictionary, you’ll get a KeyError:
print(my_dict['Random']) # Output: KeyError: 'Random'
To avoid this, you can use the get() method, which returns None if the key does not exist:
print(my_dict.get('Random', None)) # Output: None
You can also specify a default value to be returned if the key does not exist:
print(my_dict.get('Random', 'Unknown')) # Output: Unknown
One of the most powerful features of dictionaries is their ability to iterate over the keys and values. You can use the items() method to iterate over the key-value pairs:
for name, age in my_dict.items():
print(name, age)
This will output:
Sachin 25
Virat 20
Ramesh 35
You can also iterate over just the keys or just the values using the keys() and values() methods, respectively:
for name in my_dict.keys():
print(name)
for age in my_dict.values():
print(age)
Dictionaries are also mutable, which means you can add, modify, or remove key-value pairs. To add a new key-value pair, simply assign a value to a new key:
my_dict['Random'] = 40
To modify the value of an existing key, simply assign a new value to the key:
my_dict['Virat'] = 26
To remove a key-value pair, use the del statement:
del my_dict['Sachin']
You can also use the pop() method to remove a key-value pair and return its value:
age = my_dict.pop('Ramesh')
print(age) # Output: 30
Dictionaries are often used to count the frequency of elements in a list. You can do this easily using a for loop and a dictionary:
my_list = ['a', 'b', 'a', 'c', 'b', 'a']
freq_dict = {}
for element in my_list:
freq_dict[element] = freq_dict.get(element, 0) + 1
print(freq_dict) # Output: {'a': 3, 'b': 2, 'c': 1}
Dictionary Comprehension
Dictionary comprehension is a concise way of creating dictionaries in Python. It allows you to create a dictionary from any iterable object like a list, tuple, or another dictionary, by specifying the key-value pairs using a compact syntax.
The general syntax for dictionary comprehension is as follows:
{key_expression: value_expression for expression in iterable}
The key_expression is an expression that generates the key for each key-value pair, while the value_expression generates the value. The expression is evaluated for each item in the iterable, and the resulting key-value pairs are collected into a dictionary.
Here’s an example of creating a dictionary using dictionary comprehension:
numbers = [1, 2, 3, 4, 5]
squares = {n: n**2 for n in numbers}
print(squares) # Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
In this example, we create a dictionary that maps each number in the list to its square using dictionary comprehension.
You can also use conditional statements in dictionary comprehension to filter the items in the iterable before creating the dictionary. Here’s an example:
numbers = [1, 2, 3, 4, 5]
even_squares = {n: n**2 for n in numbers if n % 2 == 0}
print(even_squares) # Output: {2: 4, 4: 16}
In this example, we create a dictionary that maps each even number in the list to its square by using a conditional statement in the dictionary comprehension.
Dictionary comprehension is a concise and readable way of creating dictionaries in Python. It’s especially useful when you need to create a dictionary from an iterable object or filter the items in the iterable before creating the dictionary.
Creating a dictionary from two lists:
keys = ['a', 'b', 'c']
values = [1, 2, 3]
d = {k: v for k, v in zip(keys, values)}
print(d) # Output: {'a': 1, 'b': 2, 'c': 3}
In this example, we create a dictionary by zipping together two lists of keys and values, and using dictionary comprehension to create the key-value pairs.
Creating a dictionary with default values:
keys = ['a', 'b', 'c']
d = {k: 0 for k in keys}
print(d) # Output: {'a': 0, 'b': 0, 'c': 0}
In this example, we create a dictionary with default values of 0 for each key in the list.
Converting a dictionary to another dictionary:
d = {'a': 1, 'b': 2, 'c': 3}
d2 = {k.upper(): v for k, v in d.items()}
print(d2) # Output: {'A': 1, 'B': 2, 'C': 3}
In this example, we create a new dictionary by converting the keys to uppercase and keeping the values the same.
Creating a dictionary with filtered values:
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
d2 = {k: v for k, v in d.items() if v % 2 == 0}
print(d2) # Output: {'b': 2, 'd': 4}
Merge Dictionary
There are several ways to merge two dictionaries in Python, depending on your needs. Here are some of the most common methods:
Using the update()
method:
The update()
method is used to add the key-value pairs of one dictionary to another. If the second dictionary has keys that already exist in the first dictionary, their values will be updated.
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
dict1.update(dict2)
print(dict1) # Output: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
In this example, we use the update()
method to merge dict2
into dict1
.
Using the **
operator:
The **
operator can be used to merge two dictionaries into a new dictionary. This method creates a new dictionary, so the original dictionaries are not modified.
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged_dict = {**dict1, **dict2}
print(merged_dict) # Output: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
In this example, we use the **
operator to merge dict1
and dict2
into a new dictionary merged_dict
.
Using the dict()
constructor:
The dict()
constructor can be used to merge two dictionaries. This method also creates a new dictionary, so the original dictionaries are not modified.
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged_dict = dict(dict1, **dict2)
print(merged_dict) # Output: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
In this example, we use the dict()
constructor to merge dict1
and dict2
into a new dictionary merged_dict
.
Each of these methods has its own advantages and disadvantages depending on your specific use case. Choose the method that best suits your needs
Time Complexity
Here are some common dictionary methods in Python and their time complexity:
Method | Complexity | Description |
---|---|---|
get(key, default=None) |
O(1) | This method returns the value for a given key if it exists in the dictionary, otherwise it returns the default value. The time complexity of this method is O(1) in the average case, and O(n) in the worst case if there are many collisions. |
pop(key, default=None) |
O(1) | This method removes the key-value pair for a given key from the dictionary and returns its value. If the key is not found in the dictionary, it returns the default value. The time complexity of this method is O(1) in the average case, and O(n) in the worst case if there are many collisions. |
items() |
O(n) | This method returns a view object of the key-value pairs in the dictionary. The time complexity of this method is O(n), where n is the number of key-value pairs in the dictionary. |
keys() |
O(n) | This method returns a view object of the keys in the dictionary. The time complexity of this method is O(n), where n is the number of keys in the dictionary. |
values() |
O(n) | This method returns a view object of the values in the dictionary. The time complexity of this method is O(n), where n is the number of values in the dictionary. |
update() |
O(n) | This method updates the dictionary with key-value pairs from another dictionary, or with key-value pairs from an iterable of key-value pairs. The time complexity of this method is O(n), where n is the number of key-value pairs being added to the dictionary. |
clear() |
O(1) | This method removes all key-value pairs from the dictionary. The time complexity of this method is O(1). |
copy() |
O(n) | This method returns a shallow copy of the dictionary. The time complexity of this method is O(n), where n is the number of key-value pairs in the dictionary. |
It’s important to note that the time complexity of dictionary operations can be affected by the hash function used for the keys. If the hash function produces a lot of collisions, the worst-case time complexity of some operations may be higher. However, in practice, this is rarely a problem.