# Working with Python Dictionary

A dictionary is one of the many built-in datatypes of Python, A dictionary allows us to store data in key-value pairs, where the key must be unique. it is also known as “associative memories” or “associative arrays” in some other programming languages. A dictionary contains comma-separated `key:value` pairs enclosed within {}. In a dictionary, the different keys can have the same values but the key must be unique.

## Creating Dictionary

**Using Curly Braces** `{}`**:**

To create a dictionary in Python we use a pair of curly brackets `{}` and store comma-separated key-value pairs in it.

```python
# empty dictionary
my_dict = {}
print(type(my_dict)) # <class 'dict'>

# storing value in dictionary
my_dict = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
print(my_dict) # my_dict = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
```

**Note:**

Dictionaries in Python must have unique keys. When the same key is provided multiple times, the last one overwrites the previous one.

```python
user = {
    'name': 'John',
    'age': 25,
    'email': 'john.doe@example.com',
    "name": "James" # using the same key
}
print(user) # {'name': 'James', 'age': 25, 'email': 'john.doe@example.com'}
```

**Using** `dict()` **Constructor:**

We can also use `dict()` constructor to create a dictionary, and to store some data in it. We can pass key-value pairs as arguments or as a list of tuples. Take a look at the examples below.

```python
my_dict = dict()
print(type(my_dict)) # <class 'dict'>

# stroing data as arguments
my_dict = dict(key1='value1', key2='value2', key3='value3')
print(my_dict) # {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}

# stroring data as list of tuples
my_dict = dict([('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3')])
print(my_dict) # {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
```

## Accessing Data from Dictionary

### **Using** Square **Brackets:**

We can use square brackets with keys to access its corresponding values. If the key is not present in the dictionary a `KeyError` will be raised.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

print(user['name']) # John Doe
print(user['age']) # 25
print(user['email']) # john.doe@example.com

print(user['phone']) # KeyError
```

### **Using** `get()` **method:**

The `get()` method allows us to retrieve the value associated with the key, if the key is not present in the dictionary then it returns `None` or the `default_value` if it is specified.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

print(user.get('name')) # John Doe
print(user.get('age')) # 25
print(user.get('email')) # john.doe@example.com

print(user.get('phone')) # None
print(user.get('phone','No Phone Number')) # No Phone Number
```

### **Accessing keys of the dictionary:**

To get all the keys of a dictionary we use `dict.keys()` method. It returns a view object that contains all the keys of the dictionary. This view object is a dynamic view, meaning it reflects any changes made to the dictionary, If we add or remove an item to the dictionary, the view will automatically update. we can also use `dict.keys()` method to iterate over the keys of a dictionary using a for loop or we can use it to test the membership of any key in the dictionary. Take a look at the example below to learn about different actions we can perform using the `dict.keys()` method.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}
print(user.keys()) # dict_keys(['name', 'age', 'email'])

# adding a new key to the dictionary
user['phone'] = '9898989898'
print(user.keys())  # dict_keys(['name', 'age', 'email', 'phone'])

# Iterating over the keys of a dictionary
for i in user.keys():
  print(i)

# Testing membership
if 'phone' in user.keys():
  print("True")
else:
  print("False")

# converting the view object to a static list
static_list = list(user.keys())
print(static_list)  # ['name', 'age', 'email', 'phone']
```

### Accessing Values of the dictionary.

To access all the values of the dictionary we use the `dict.values()` method. It returns a view object that contains all the values of the dictionary. This view object is a dynamic view, meaning it reflects any changes made to the dictionary, If we add or remove an item to the dictionary, the view will automatically update. The view object can be iterated and it also supports membership testing, which means we can use `dict.values()` the method to iterate over the values of the dictionary or to test the membership of some value in the dictionary. Take a look at the example below.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}
print(user.values())  # dict_values(['John Doe', 25, 'john.doe@example.com'])

# adding a new item to the dictionary
user['phone'] = '9898989898'
print(user.values())  # dict_values(['John Doe', 25, 'john.doe@example.com', '9898989898'])

# Iterating over the keys of a dictionary
for i in user.values():
  print(i)

# Testing membership
if 25 in user.values():
  print("True")
else:
  print("False")

# converting the view object to a static list
static_list = list(user.values())
print(static_list)  # ['name', 'age', 'email', 'phone']
```

### Accessing items from the dictionary

we use `dict.items` method to access all the items from the dictionary. It returns a view object that contains a list of tuples with key-value pairs of the dictionary. The view object is the same as we learned in `dict.keys() or dict.values()` methods. Take a look at the example below.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}
print(user.items())  # dict_items([('name', 'John Doe'), ('age', 25), ('email', 'john.doe@example.com')])

# adding a new item to the dictionary
user['phone'] = '9898989898'
print(user.items())  # dict_items([('name', 'John Doe'), ('age', 25), ('email', 'john.doe@example.com'), ('phone', '9898989898')])

# Iterating over the keys of a dictionary
for key, value in user.items():
  print(key, value)

# Testing membership
if ('age', 25) in user.items():
  print("True")
else:
  print("False")

# conveting the object view to static list
static_list = list(user.items())
print(static_list)  # [('name', 'John Doe'), ('age', 25), ('email', 'john.doe@example.com'), ('phone', '9898989898')]
```

## Modifying Dictionary

While working with a dictionary, we will need to add new data, delete existing data, or update the existing data from the dictionary, in this section of the article we will learn about this.

### Adding new data

We can add new key-value pairs to the dictionary either by using square brackets or by using `dict.update()` method let's take a look at them one by one. Any new `key:value` pair added to the dictionary will be added at the end of the dictionary since the dictionary maintains the order of its items.

**Using Square Brackets:**

> Syntax :-
> 
> dict\[key\] = value

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# adding new key value pair
user['phone'] ='9898989898'
user['username'] = 'john'

print(user) # {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com', 'phone': '9898989898', 'username': 'john'}
```

**Using the update method:**

The `update()` method is used to update a dictionary with elements from another dictionary or from an iterable of key-value pairs (as tuples or other iterable of length two) or keyword arguments. This method modifies the dictionary in place, adding new key-value pairs or if the key already exits it updates the value of it.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# updating with another dictionary 
extra_data = {"phone": "989898989", "username": "john"}
user.update(extra_data)
print(user)  # {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com', 'phone': '989898989', 'username': 'john'}

# updating with list of tuples
extra_data = [("phone", "989898989"), ("username", "john")]
user.update(extra_data)
print(user)  # {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com', 'phone': '989898989', 'username': 'john'}

# updating with keyword arguments
user.update(phone="989898989", username="john")
print(user)  # {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com', 'phone': '989898989', 'username': 'john'}

# updating values of a key 
user.update(phone="989898989", username="john",age=30)
print(user)  # {'name': 'John Doe', 'age': 30, 'email': 'john.doe@example.com', 'phone': '989898989', 'username': 'john'}
```

### Removing data from the dictionary

**Using del statement**

`del` statement is used to remove key-value pairs from the dictionary. It takes a key and removes the key and its corresponding value from the list. if the key is not present in the dictionary it raises `KeyError`. We can even delete the complete dictionary using the `del` statement.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# removing item with del statement
del user['name']
print(user) # {'age': 25, 'email': 'john.doe@example.com'}

# trying to delete item that doesn't exists in the dictionary
del user['phone'] # KeyError: 'phone'

# removing the complete dictionary
del user
```

**Using the** `pop()` **method:**

`pop()` methods accept a key and optional default value, it removes the specified key from the dictionary and returns its value. If the key is not in the dictionary it returns the default value if it is specified else it raises a key error.

> Syntax:
> 
> dict.pop(key,default\_value)
> 
> * `key`: The key whose associated value is to be retrieved and removed.
>     
> * `default` (optional): If the key is not found, the default value is returned (if provided). If not provided and the key is not found, a `KeyError` is raised.
>     

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# using pop method to remove item from the list
returned_value = user.pop('name')
print(returned_value) # John Doe

# using default value
returned_value = user.pop('phone',"Invalid key")
print(returned_value) # invalid key

# using invalid key without default value
returned_value = user.pop('username')
print(returned_value) # KeyError

print(user) # {'age': 25, 'email': 'john.doe@example.com'}
```

**Using** `dict.clear()` **method:**

`dict.clear()` method is used to remove all the key-value pairs from the dictionary and returns `None`.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# using clear method
returned_value = user.clear()
print(returned_value) # None
print(user) # {}
```

## Using Loops with Dictionary

**Note:**

The dictionary preserves the insertion order, which means the order in which items are added to a dictionary will be maintained, when we iterate through the items of a dictionary, they will be in the same order in which they were inserted. However, we can't access the items in the dictionary using their position because items in the dictionary are key-indexed not position-indexed.

**Using for loop with dictionary:**

When we loop over the dictionary it implicitly loops over its keys.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

for val in user:
  print(val)

# Output
'''
name
age
email
'''
```

but we can use these keys to access their corresponding values.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

for val in user:
  print(user[val])

# Output
'''
John Doe
25
john.doe@example.com
'''
```

**Looping through keys or values.**

As we have seen in this article earlier we can user `dict.values()` to get values of the list and `dict.keys()` to get list of keys.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# looping through keys
for key in user.keys():
  print(key)

# looping through values
for val in user.values():
  print(val)
```

**Looping through key-value pairs**

We can use `dict.items()` to loop through the key-value pairs of the dictionary. `dict.items()` method returns tuples of key-value pairs of a dictionary.

```python
user = {'name': 'John Doe', 'age': 25, 'email': 'john.doe@example.com'}

# looping through key-value pairs
for key,value in user.items():
  print(f"Key: {key}, Value: {value}")
```

## Dictionary Comprehension

**Comprehensions:**

Comprehension in Python is syntactic sugar, it allows us compact and shorter syntax to create new sequences (set, list, dictionary). Take a look at a simple example below.

> Syntax:
> 
> dict = {key: value for i in iterable}

```python
# Create a dictionary with squares of numbers from 1 to 5
my_dict = {num: num**2 for num in range(1, 6)}
print(my_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
```

## Choosing the correct key

We know that keys in the dictionary must be unique, but what are the possible candidates for keys?

Any immutable type can serve as a key, such as a number, string, or tuple. If you use a tuple as a key, it must only contain immutable types such as a number, string, or another tuple.

As another way to put it, keys need to be hashable to be used in dictionaries. Hashable means that the value should have a fixed hash value that doesn't change during its lifetime.

```python
# Using numbers as keys
number_dict = {1: 'One', 2: 'Two', 3: 'Three'}

# Using strings as keys
string_dict = {'apple': 1, 'banana': 2, 'orange': 3}

# Using a tuple with immutable elements as a key
tuple_dict = {('John', 25): 'Student', ('Alice', 30): 'Teacher', ('Bob', 22): 'Engineer'}
```

## Dictionary methods

| **Method** | **Description** |
| --- | --- |
| `dict.keys()` | Returns keys view |
| `dict.values()` | Returns values view |
| `dict.items()` | Returns key-value pairs view |
| `dict.get(key, default)` | Returns value for key, or default if key not found |
| `dict.pop(key, default)` | Removes and returns value for key |
| `dict.popitem()` | Removes and returns the last key-value pair |
| `dict.update(other_dict)` | Updates the dictionary with another dictionary |
| `dict.clear()` | Removes all items from the dictionary |

## Summary:-

* Python dictionaries are collections that store data in key-value pairs, enclosed in curly braces `{}`.
    
* Keys in a dictionary must be unique, and dictionaries are mutable (modifiable).
    
* Creating dictionaries can be done using curly braces `{}` or the `dict()` constructor.
    
* Accessing data in a dictionary is possible through square brackets or the `get()` method.
    
* Modifying dictionaries involves adding, removing, or updating key-value pairs.
    
* Keys in dictionaries must be hashable, and any immutable type can be used as a key.
    
* Loops can be used with dictionaries, implicitly iterating over keys or explicitly using methods like `keys()`, `values()`, or `items()`.
    
* Dictionary comprehension provides a concise syntax for creating dictionaries in a single line.
    
* The dictionary preserves the insertion order, which means the order in which items are added to a dictionary will be maintained, when we iterate through the items of a dictionary, they will be in the same order in which they were inserted. However, we can't access the items in the dictionary using their position because items in the dictionary are key-indexed not position-indexed.
    
* using `len(dict)` function with a dictionary will return the number of `key:value` pairs in the dictionary.
    
* using `sorted(dict)` function will return a sorted list of keys.
    
* using `max(dict)` function will return the maximum key in the dictionary.
    
* using `min(dict)` function will return the minimum key in the dictionary.
    
* using `sum(dict)` function will return the sum of all the keys if the keys are numbers.
