18. Dictionary type#

Author: Tue Nguyen

18.1. Outline#

  • What is a dictionary?

  • Create a dictionary

  • Elements of a dict

  • Iterate through a dictionary

  • Copy a dictionary

  • Operations on dictionaries

  • Dict comprehensions

18.2. What is a dictionary?#

  • A dictionary (or dict) is used to store data in the form of key: value pairs

  • Each element of a dict is has a key part and a value part

  • Elements are accessed using key

  • Dicts are mutable

  • The keys must be unique and of an immutable type

  • However, the values can be of any types

  • For Python <= 3.6, dicts are not ordered

  • For Python >= 3.7, dicts are insertion ordered

18.3. Create a dict#

18.3.1. Use {}#

# A dict with one element
d = {"name": "John"}
print(d)
print(type(d))
{'name': 'John'}
<class 'dict'>
# For a dict with multi elements
# We can span more than 1 line for readability
d = {
    "name": "John",
    "age": 20,
    "country": "UK"
}

print(d)
{'name': 'John', 'age': 20, 'country': 'UK'}

18.3.2. Use dict()#

d = dict(
    name="John",
    age=20,
    country="UK"
)

print(d)
{'name': 'John', 'age': 20, 'country': 'UK'}

18.4. Elements of a dict#

18.4.1. Keys, values, and items#

# Recall the dict
d
{'name': 'John', 'age': 20, 'country': 'UK'}
# Get keys
d.keys()
dict_keys(['name', 'age', 'country'])
# Get values
d.values()
dict_values(['John', 20, 'UK'])
# View elements as a sequence of tuples
# Keys are the first elements in the tuples
# We usually use this when iterate through a list
d.items()
dict_items([('name', 'John'), ('age', 20), ('country', 'UK')])

18.4.2. Get values by keys#

# Get value associated with name
d["name"]
'John'
# Get value associated with country
d["country"]
'UK'

18.4.3. Add and update elements#

We use assignment to add a new elements or update the value for an exisiting element

# Init
d = {"name": "John", "age": 20}
d
{'name': 'John', 'age': 20}
# Add education
d["educ"] = "BSc"
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}
# Now update education to MSc
d["educ"] = "MSc"
d
{'name': 'John', 'age': 20, 'educ': 'MSc'}

18.5. Dict keys#

a) Dict keys must be of immutable types

# Int keys
{1: True, 2: "A"}
{1: True, 2: 'A'}
# Float keys are possible but not recommended
# Because computer store floats approximately
{1.2: True, 5.: "B"}
{1.2: True, 5.0: 'B'}
# String keys
{"name": "John", "age": 20}
{'name': 'John', 'age': 20}
# Tuple keys
d = {
    ("US", "gdp"): 19.49,
    ("US", "pop"): 325,
    ("CN", "gdp"): 12.24,
    ("CN", "pop"): 1421
}

print(d[("US", "gdp")]) # GDF of US
print(d[("CN", "pop")]) # Population of china
19.49
1421

b) Dict keys must be unique

# Try to duplicate a key, only the latter is kept
{"name": "John", "age": 20, "age": 30}
{'name': 'John', 'age': 30}

18.6. Conversion to dicts#

We use dict() to convert an object to a dict

# Convert a sequence of 2-element squences
data = [("name", "John"), ("age", 20)]
dict(data)
{'name': 'John', 'age': 20}
# Convert a zipped object to a dict
names = ["A", "B"]
ages = [20, 30]
dict(zip(names, ages))
{'A': 20, 'B': 30}

18.7. Iterate through a dict#

# Init a dict
d = {"name": "John", "age": 20, "educ": "MSc"}
d
{'name': 'John', 'age': 20, 'educ': 'MSc'}

18.7.1. Iterate through keys#

# Method 1
for k in d:
    print(k)
name
age
educ
# Method 2
for k in d.keys():
    print(k)
name
age
educ

18.7.2. Iterate through values#

for v in d.values():
    print(v)
John
20
MSc

18.7.3. Iterate through both keys and values#

# Naive way
for k in d:
    print(f"Key {k} has value {d[k]}")
Key name has value John
Key age has value 20
Key educ has value MSc
# Better way
for k, v in d.items():
    print(f"Key {k} has value {v}")
Key name has value John
Key age has value 20
Key educ has value MSc

What happened?

  • Recall that each element of d.items() is 2-element tuple

  • Thus writing k, v in the for loop means we unpack the corresponding tuple at each iteration

  • Thus the key goes to k and the value goes to v

18.8. Mutablity#

  • A dict is mutable

  • We can add, update, or delete elements

18.9. Copy a dict#

Since the dict type is mutable, copying a dict has the same behavior as copying a list

18.9.1. Shallow copying#

# Init a dict
d = {"name": "John", "degrees": ["BSc", "MSc"]}
print(d)
print(id(d))
{'name': 'John', 'degrees': ['BSc', 'MSc']}
139923067098176
# Make a shallow copy
d2 = d.copy()
# Verify d and d2 have the same values
print(d)
print(d2)
print(d == d2)
{'name': 'John', 'degrees': ['BSc', 'MSc']}
{'name': 'John', 'degrees': ['BSc', 'MSc']}
True
# But d and d2 are pointing to different objects
print(id(d))
print(id(d2))
print(d is d2)
139923067098176
139923101568704
False
# Changing name of d (a string - immutable) 
# will not change name of d2
d["name"] = "Jack"
print(d)
print(d2)
{'name': 'Jack', 'degrees': ['BSc', 'MSc']}
{'name': 'John', 'degrees': ['BSc', 'MSc']}
# But modifying degrees of d (a list - mutable) 
# will also modify that of d2
d["degrees"][0] = "PhD"
print(d)
print(d2)
{'name': 'Jack', 'degrees': ['PhD', 'MSc']}
{'name': 'John', 'degrees': ['PhD', 'MSc']}

18.9.2. Deep copying#

Same as lists, we use copy.deepcopy()

# Init
d = {"name": "John", "degrees": ["BSc", "MSc"]}
d
{'name': 'John', 'degrees': ['BSc', 'MSc']}
# Import module copy
import copy
# Make a deep copy
d2 = copy.deepcopy(d)
# Change degrees of d, d2 stays the same
d["degrees"][0] = "PhD"
print(d)
print(d2)
{'name': 'John', 'degrees': ['PhD', 'MSc']}
{'name': 'John', 'degrees': ['BSc', 'MSc']}

18.10. Operations on a dict#

As a mutable type like lists, we also have two types of operations on dicts

  • Regular operations

  • Inplace operations

18.10.1. Regular operations#

# Init a dict
d = {"name": "John", "age": 20, "educ": "BSc"}
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}

Get value by key

# Traditional way
d["name"]
'John'
# However, if the key is not in d, we will get an error
# Try d["weight"]
# To avoid an error, we can use .get
# If the key is not in d, we will get a None back
w = d.get("weight")
print(w)
None
# If we don't like None, we can set the default value
d.get("weight", "N/A")
'N/A'

Count number of elements

len(d)
3

Member ship checking

# Check if d has a key "age"
"age" in d
True
# Check if d has a key "address"
"address" in d
False
# Check if d has a value 20
20 in d.values()
True

18.10.2. Inplace operations#

# Init a dict
d = {"name": "John", "age": 20, "educ": "BSc"}
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}

Add or update elements

# Add country
d["country"] = "UK"
d
{'name': 'John', 'age': 20, 'educ': 'BSc', 'country': 'UK'}
# Update country to "US"
d["country"] = "US"
d
{'name': 'John', 'age': 20, 'educ': 'BSc', 'country': 'US'}

Delete an element

# Use del to delete by key
del d["country"]
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}
# However, if key is not in d, we will get an error
# Try del d["weight"] to see it
# We can use an if statement to avoid this
if "weight" in d:
    del d["weight"]
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}

Set default

Sometimes, we are not sure if a key already exists

  • If it exists, we want to leave it as it is

  • If not, we want to add a new element with the key and a default value

# Init a dict
d = {"name": "John", "age": 20, "educ": "BSc"}
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}
# If key "age" exists, leave it as it is
# If not, add a new element with that key and value -1
d.setdefault("age", -1)
d
{'name': 'John', 'age': 20, 'educ': 'BSc'}
# If key "credits" exists, leave it as it is
# If not, add a new element with that key and value 0
d.setdefault("credits", 0)
d
{'name': 'John', 'age': 20, 'educ': 'BSc', 'credits': 0}

Pop an element

# We can pop a value by key
# Also note that if the key is not in d, we will get an error
educ = d.pop("educ")
print(educ)
print(d)
BSc
{'name': 'John', 'age': 20, 'credits': 0}

Update a dict

# Init 2 dict
d = {"name": "John", "age": 20}
d2 = {"age": 22, "educ": "BSc"}
print(d)
print(d2)
{'name': 'John', 'age': 20}
{'age': 22, 'educ': 'BSc'}
# Update d with info from d2
# Key "educ" is new to d -> new element is created for this key
# Key "age" already exists in d -> update the value for this key from d2
d.update(d2)
d
{'name': 'John', 'age': 22, 'educ': 'BSc'}

18.11. Dict comprehensions#

  • Dict comprehensions are similar to list comprehensions

  • Used to quickly make a dict from an iterable

  • Syntax 1 (without filtering): {key_expr: value_expr for item in iterable}

  • Syntax 2 (with filtering): {key_expr: value_expr for item in iterable if condition}

# Make a dict from a list of words
# Keys are words in lowercase
# Values are words in uppercase
words = ["Cat", "Dog", "Cow", "Duck"]
{w.lower(): w.upper() for w in words}
{'cat': 'CAT', 'dog': 'DOG', 'cow': 'COW', 'duck': 'DUCK'}
# From list words above, make a dict
# Keys are the position of the word
# Value are the words themselves
{(i+1): v for i, v in enumerate(words)}
{1: 'Cat', 2: 'Dog', 3: 'Cow', 4: 'Duck'}
# Make a dict
# Keys are integers from 1 to 5
# Values are squares of keys
{v:v**2 for v in range(1, 6)}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# From list words above, make a list
# Keys are words
# Values are the numbers of characters
# Only keep words with <= 3 characters
{w: len(w) for w in words if len(w) <= 3}
{'Cat': 3, 'Dog': 3, 'Cow': 3}

18.12. Summary#

What is a dictionary?

  • Mutable object storing data in the form of key: value pairs

  • Each element of a dict is has a key part and a value part

  • Elements are accessed using key

  • The keys must be unique and of an immutable type

  • However, the values can be of any types

  • For Python <= 3.6, dicts are not ordered

  • For Python >= 3.7, dicts are insertion ordered

Create a dict

  • Use {} or dict()

Iterate through a dictionary

  • Iterate through keys: for k in d

  • Iterate through values: for v in d.values()

  • Iterate through items: for k, v in d.items()

Copy a dictionary

  • Same as lists (there are both shallow and deep copying)

Operations on dicts

  • Regular operations

    • Get value by key: d["name"], d.get("name")

    • Count number of elements: len(d)

    • Membership checking: "name" in d, "John" in d.values()

  • Inplace operations

    • Add or update an element: d["name"] = "Jack"

    • Delete an element: del d["name"]

    • Set default: d.setdefault("name", "N/A")

    • Pop an element: name = d.pop("name")

    • Update a dict: d.update(d2)

Dict comprehensions

  • Similar to list comprehensions

  • Syntax 1: {key_expr: value_expr for item in iterable}

  • Syntax 2: {key_expr: value_expr for item in iterable if condition}

18.13. Practice#

To be updated