Python How to create a missing key automatically

eye-catch Python
Sponsored links

Creating a dictionary

There are two ways to create a dictionary.

empty_dict1 = {}
empty_dict2 = dict()
# empty_dict1: True, {}
print(f"empty_dict1: {isinstance(empty_dict1, dict)}, {empty_dict1}")
# empty_dict2: True, {}
print(f"empty_dict2: {isinstance(empty_dict2, dict)}, {empty_dict2}")

Just using {} or dict() class explicitly.

data_dict = {1: "Apple", 2: "Orange", 3: "Strawberry"}
data_dict2 = dict(one="Apple", two="Orange")
# {1: 'Apple', 2: 'Orange', 3: 'Strawberry'}
print(data_dict)
# {'one': 'Apple', 'two': 'Orange'}
print(data_dict2)

If you want to use number as a key, you can’t use dict() constructor because it accepts only string.

print({1.1: 1})     # OK
print(dict(1=1))    # NG
print(dict(1.1=1))  # NG
Sponsored links

Read a value of a key

A value can be read by specifying the key in the square brackets.

# 1: Apple, 2: Orange, 3: Strawberry
print(f"1: {data_dict[1]}, 2: {data_dict[2]}, 3: {data_dict[3]}")
# one: Apple, two: Orange
print(f"one: {data_dict2['one']}, two: {data_dict2['two']}")

Note that double quotes can’t be used for string key. It must be single quotes.

data_dict2["one"] # NG

It’s also possible to use get method instead.

# 1: Apple
print(f"1: {data_dict.get(1)}")

Difference between get method and brackets

Let’s check the difference. Reading a value with brackets can raise an KeyError.

try:
    print(data_dict[999])
except KeyError as err:
    # KeyError: 999
    print(f"KeyError: {err}")

# 1: None
print(f"1: {data_dict.get(999)}")
# 1: default-value 999
print(f"1: {data_dict.get(999, 'default-value 999')}")

But get() method doesn’t raise an error. If the key doesn’t exist, it returns None by default. If you need your own default value, just specify it to the second parameter.

Add an element

Adding an element is simple.

data_dict[4] = "Blueberry"
print(data_dict)
# {1: 'Apple', 2: 'Orange', 3: 'Strawberry', 4: 'Blueberry'}

Update an element

Update an element is the same as adding an element. If the key exists, the value is updated. Otherwise, added.

data_dict[4] = "Rasberry"
print(data_dict)

Add/Update multiple elements

Use update method if multiple elements need to be updated/added at once.

# {1: 'Apple', 2: 'Orange', 3: 'Strawberry', 4: 'Rasberry'}
print(data_dict)
data_dict.update({1: "Green Apple", 4: "Riped Rasberry", 5: "Something", 6: "Empty"})
# {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 4: 'Riped Rasberry', 5: 'Something', 6: 'Empty'}
print(data_dict)

If the key is string, the key-value pairs can be added as follows.

# {'one': 'Apple', 'two': 'Orange'}
print(data_dict2)
data_dict2.update({"eight":"value 8"}, nine="value 9", ten="value 10")
# {'one': 'Apple', 'two': 'Orange', 'eight': 'value 8', 'nine': 'value 9', 'ten': 'value 10'}
print(data_dict2)

Delete an element

An element can be deleted with del keyword but an error is raised if the key doesn’t exist.

try:
    # {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 4: 'Riped Rasberry', 5: 'Something', 6: 'Empty'}
    print(data_dict)
    del data_dict[4]
    # {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 5: 'Something', 6: 'Empty'}
    print(data_dict)
    del data_dict[4]
except KeyError as err:
    print(f"KeyError occurred. Key: {err}")

Use clear() method if it’s necessary to delete all the elements

copied_dict = data_dict.copy()
# {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 5: 'Something', 6: 'Empty'}
print(data_dict)
data_dict.clear()
# {}
print(data_dict)

Get a key-value pair and delete it at once

Use pop() method if you need to remove the element after reading it.

# {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 5: 'Something', 6: 'Empty'}
print(data_dict)
pop_data = data_dict.pop(6)
# Empty
print(pop_data)
# {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 5: 'Something'}
print(data_dict)

If you want to try to get the key-value pair anyway without knowing if the key exists, specify the default value to the second parameter.

pop_data = data_dict.pop(99999, "DEFAULT-VALUE")
print(pop_data) # DEFAULT-VALUE

If it exists, the actual value is returned. Otherwise, default value.

Get the last value and delete it

If you don’t need to specify a key and just want to get a pair and delete it one by one, you can use popitem().

data_dict3 = dict(one="value1", two="value2", three="value3", four="value4")
# ('four', 'value4')
print(data_dict3.popitem())
# {'one': 'value1', 'two': 'value2', 'three': 'value3'}
print(data_dict3)
data_dict3.popitem()
data_dict3.popitem()
data_dict3.popitem()
# {}
print(data_dict3)
try:
    data_dict3.popitem()
except Exception as err:
    # 'popitem(): dictionary is empty'
    print(err)

Check if a key exists in a dictionary

To check if a key is included in the dictionary, in keyword can be used.

for key in [1, 3, 4]:
    if key in copied_dict:
        print(f"data_dict contains key {key}")
    else:
        print(f"data_dict doesn't contain key {key}")

# data_dict contains key 1
# data_dict contains key 3
# data_dict doesn't contain key 4

Get the first key, value, key-value pair

Dictionary has keys(), values(), and items() but it can’t directly be read by using square brackets. It needs to be wrapped with list first.

# {1: 'Green Apple', 2: 'Orange', 3: 'Strawberry', 5: 'Something', 6: 'Empty'}
print(copied_dict)
# 1
print(list(copied_dict.keys())[0])
# Green Apple
print(list(copied_dict.values())[0])
# (1, 'Green Apple')
print(list(copied_dict.items())[0])

Create a key-value automatically if it doesn’t exist

In some cases, we need to assign a default value if the key doesn’t exist in the dict. Let’s assume that we want to create a histogram. The key is int and the value is also int to indicate how many times the number appears.

We need to implement it in the following way.

count_dict: Dict[int, int] = {}

for num in range(10):
    num = int(random.random() * 5)
    if num not in count_dict:
        print(f"new key was created [{num}]")
        count_dict[num] = 0
    count_dict[num] += 1

for key, value in count_dict.items():
    print(f"key: {key}, value: {value}")

The main issue here is to assign a value to the key. In other words, we must create the key before incrementing the current value. The result looks as follows.

new key was created [3]
new key was created [1]
new key was created [0]
key: 3, value: 6
key: 1, value: 3
key: 0, value: 1

To make the code clearer, we can use defaultdict that automatically creates the key with the default value. We don’t have to care whether the key exists or not. The following code doesn’t contain if condition and thus the code is more readable.

from collections import defaultdict

count_dict: Dict[int, int] = defaultdict(int)

for num in range(10):
    num = int(random.random() * 5)
    count_dict[num] += 1

for key, value in count_dict.items():
    print(f"key: {key}, value: {value}")

# key: 0, value: 2
# key: 4, value: 6
# key: 3, value: 1
# key: 2, value: 1

defaultdict accepts a callback to return the default value. If we want to assign an arbitrary value, we can pass a lambda there.

count_dict: Dict[int, int] = defaultdict(lambda : 5)

for num in range(10):
    num = int(random.random() * 5)
    count_dict[num] += 1

for key, value in count_dict.items():
    print(f"key: {key}, value: {value}")

# key: 1, value: 9
# key: 3, value: 8
# key: 2, value: 6
# key: 0, value: 7

As you can see from the result, the sum of the values is bigger than 10. If you are not familiar with lambda, check the following post.

Sort a dict

If a dict needs to be sorted, sorted() function must be used. Note that a dict can’t be passed directly but dict.items() needs to be specified as a first parameter. key property requires a callback for the sorting logic.

dict_data = {
    "1": {"id": 2, "value": "value 2"},
    "2": {"id": 5, "value": "value 5"},
    "3": {"id": 1, "value": "value 1"},
    "4": {"id": 3, "value": "value 3"},
}

sorted_dict = sorted(dict_data.items(), key=lambda pair: pair[1]["id"])

for entry in sorted_dict:
    print(entry)
# ('3', {'id': 1, 'value': 'value 1'})
# ('1', {'id': 2, 'value': 'value 2'})
# ('4', {'id': 3, 'value': 'value 3'})
# ('2', {'id': 5, 'value': 'value 5'})    

pair[0] is a key and pair[1] is a value.

Dict to string/json

The dict needs to be converted to str if it needs to be written to a log or file. How can we convert a dict to str?

Convert dict to str by str()

The easiest way is to use str.

data = {
    "pineapple": 10,
    "apple juice": 4,
    "orange": 6,
    "water melon": 11
}
# <class 'str'>
print(type(str(data)))
# {'pineapple': 10, 'apple juice': 4, 'orange': 6, 'water melon': 11}
print(str(data))

All key-value pairs are printed as expected. It can convert to str without any error even if it contains a tuple in the key.

class_key = {
    (1, 2, 3): 1,
    (1): 2,
    (4): 3,
    5: 4,
}
# {(1, 2, 3): 1, 1: 2, 4: 3, 5: 4
print(str(class_key))

Convert dict to str by using json.dumps

The previous way is not nice when we want to write the dict key-value pairs to log file. It’s not readable. In this case, json.dumps() is a good choice.

It converts a dict to str in the same way as str() if only data is passed.

import json

# {"pineapple": 10, "apple juice": 4, "orange": 6, "water melon": 11}
print(json.dumps(data))

Let’s make it more readable. The output looks better with indent. Furthermore, it sorts automatically with sort_keys=True.

# {
#   "apple juice": 4,
#   "orange": 6,
#   "pineapple": 10,
#   "water melon": 11
# }
print(json.dumps(data, sort_keys=True, indent=2))

I guess this is a special case but we can change the separator too.

# {
#   "apple juice" -> 4 |
#   "orange" -> 6 |
#   "pineapple" -> 10 |
#   "water melon" -> 11
# }
print(json.dumps(data, sort_keys=True, indent=2, separators=(" |", " -> ")))

The colon is replaced with an arrow and the new line is replaced with a vertical line.

Note that json.dumps() raises a type error if the dict contains non-basic types. skipkeys=True ignores the error in this case.

class_key = {
    (1, 2, 3): 1,
    (1): 2,
    (4): 3,
    5: 4,
}

try:        
    print(json.dumps(class_key))
except TypeError as err:
    # keys must be str, int, float, bool or None, not tuple
    print(err)

# {"1": 2, "4": 3, "5": 4}
print(json.dumps(class_key, skipkeys=True))

Comments

Copied title and URL