Dictionaries and Sets in Python

Understanding Mapping and Collection Types

Dictionaries and sets are two powerful collection types in Python, each with unique features and use cases. While they both use curly braces in their syntax, they serve very different purposes.

Dictionaries are mapping types that store key-value pairs, while sets are unordered collections of unique elements with efficient membership testing.

# Dictionary stores key-value pairs
person = {"name": "Alice", "age": 30, "city": "New York"}

# Set stores unique values
fruits = {"apple", "banana", "cherry"}

# Both use curly braces but serve different purposes
print(person["name"])    # Access by key: "Alice"
print("banana" in fruits)  # Membership test: True

Python Dictionaries in Detail

Dictionary Creation and Basics

Dictionaries are Python's built-in mapping type that stores key-value pairs. Each key must be unique and immutable. They're created with curly braces {} or the dict() constructor.

# Ways to create dictionaries
empty_dict = {}
person = {"name": "Alice", "age": 30}

# Using dict constructor
config = dict(host="localhost", port=8080)

# From list of tuples
items = dict([("apple", 1.5), ("banana", 0.75)])

# From iterables with keys and values
keys = ["a", "b", "c"]
values = [1, 2, 3]
combined = dict(zip(keys, values))  # {'a': 1, 'b': 2, 'c': 3}

# Dictionary comprehension
squares = {x: x**2 for x in range(6)}  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Accessing and Modifying Dictionaries

Python offers multiple ways to access, add, update, and remove dictionary items.

# Accessing dictionary values
person = {"name": "Alice", "age": 30, "city": "New York"}

# By key
print(person["name"])  # Alice
# print(person["job"])  # KeyError if key doesn't exist

# get() method
print(person.get("name"))       # Alice
print(person.get("job"))        # None (default)
print(person.get("job", "N/A"))  # N/A (custom default)

# Adding or updating items
person["job"] = "Engineer"       # Add new key-value pair
person["age"] = 31               # Update existing value

# update() method - merge dictionaries
person.update({"job": "Developer", "email": "alice@example.com"})

# Removing items
removed_value = person.pop("city")  # Returns "New York"
# person.pop("country")  # KeyError if key doesn't exist
removed_item = person.popitem()     # Removes and returns last item as tuple
del person["job"]                  # Delete key-value pair

# Clear all items
person.clear()  # {}

Dictionary Methods and Operations

Dictionaries provide several useful methods for working with keys, values, and items.

# Dictionary views
person = {"name": "Alice", "age": 30, "city": "New York"}

# Get all keys
keys = person.keys()  # dict_keys(['name', 'age', 'city'])

# Get all values
values = person.values()  # dict_values(['Alice', 30, 'New York'])

# Get all key-value pairs as tuples
items = person.items()  # dict_items([('name', 'Alice'), ('age', 30), ('city', 'New York')])

# These views are dynamic, they update when dictionary changes
person["email"] = "alice@example.com"
print(keys)  # Now includes 'email'

# Checking if a key exists
print("name" in person)  # True
print("job" in person)   # False

# Copy a dictionary
person_copy = person.copy()
person_deep_copy = dict(person)

# setdefault() - get value or set default
score = person.setdefault("score", 0)  # Adds {"score": 0}

Dictionary Iteration

There are several ways to iterate through dictionaries in Python.

# Iterating through a dictionary
person = {"name": "Alice", "age": 30, "city": "New York"}

# Iterate through keys (default)
for key in person:
    print(key, "->", person[key])

# Iterate through values
for value in person.values():
    print(value)

# Iterate through keys
for key in person.keys():
    print(key)

# Iterate through key-value pairs
for key, value in person.items():
    print(f"{key}: {value}")

# Sorted iteration (by keys)
for key in sorted(person.keys()):
    print(f"{key}: {person[key]}")

Dictionary Comprehensions

Dictionary comprehensions provide a concise way to create dictionaries based on existing iterables.

# Basic dictionary comprehension
squares = {x: x**2 for x in range(6)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# With conditions
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
print(even_squares)  # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

# Transform existing dictionary
person = {"name": "Alice", "age": 30, "city": "New York"}
upper_dict = {k.upper(): v for k, v in person.items()}
print(upper_dict)  # {'NAME': 'Alice', 'AGE': 30, 'CITY': 'New York'}

# From two lists
fruits = ["apple", "banana", "cherry"]
prices = [1.2, 0.9, 2.5]
fruit_prices = {f: p for f, p in zip(fruits, prices)}
print(fruit_prices)  # {'apple': 1.2, 'banana': 0.9, 'cherry': 2.5}

Nested Dictionaries

Dictionary values can be any Python object, including other dictionaries.

# Nested dictionaries
users = {
    "alice": {
        "name": "Alice Smith",
        "email": "alice@example.com",
        "age": 30
    },
    "bob": {
        "name": "Bob Johnson",
        "email": "bob@example.com",
        "age": 25
    }
}

# Accessing nested values
print(users["alice"]["email"])  # alice@example.com

# Modifying nested values
users["alice"]["age"] = 31

# Adding nested dictionaries
users["charlie"] = {"name": "Charlie Brown", "email": "charlie@example.com", "age": 35}

# Iterating through nested dictionaries
for username, user_info in users.items():
    print(f"User: {username}")
    for key, value in user_info.items():
        print(f"  {key}: {value}")

# Deep vs. shallow copy
import copy
users_shallow = users.copy()
users_deep = copy.deepcopy(users)

Python Sets in Detail

Set Creation and Basics

Sets are unordered collections of unique elements in Python. They are useful for membership testing, removing duplicates, and performing mathematical set operations like unions and intersections.

# Ways to create sets
empty_set = set()  # Empty set (cannot use {} as that creates an empty dict)
fruits = {"apple", "banana", "cherry"}  # Set with initial values

# From other iterables
vowels = set("aeiou")  # {'a', 'e', 'i', 'o', 'u'}
numbers = set([1, 2, 2, 3, 4, 4, 5])  # {1, 2, 3, 4, 5} (duplicates removed)

# Set comprehension
even_numbers = {x for x in range(10) if x % 2 == 0}  # {0, 2, 4, 6, 8}

# Properties of sets
# - Sets are unordered (items have no index)
# - Sets contain unique items only
# - Sets can only contain immutable elements (no lists/dicts)
# - Sets themselves are mutable (can be changed)

Set Operations

Sets support a variety of operations for adding, removing, and modifying elements.

# Adding elements
colors = {"red", "green"}
colors.add("blue")  # {"red", "green", "blue"}
# Adding duplicate elements has no effect
colors.add("red")  # Still {"red", "green", "blue"}

# Add multiple elements
colors.update(["yellow", "purple"])  # {"red", "green", "blue", "yellow", "purple"}
colors.update({"orange", "pink"}, ["black", "white"])  # Can combine multiple iterables

# Removing elements
colors.remove("red")  # Removes "red" from the set
# colors.remove("brown")  # KeyError if element doesn't exist

colors.discard("green")  # Removes "green" from the set
colors.discard("gray")  # No error if element doesn't exist

popped = colors.pop()  # Removes and returns an arbitrary element

# Remove all elements
colors.clear()  # {}

Set Methods and Mathematical Operations

Sets in Python support various mathematical set operations like union, intersection, and difference.

# Set operations
A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}

# Union: all elements from both sets
print(A | B)  # {1, 2, 3, 4, 5, 6, 7, 8}
print(A.union(B))  # Same as above

# Intersection: elements common to both sets
print(A & B)  # {4, 5}
print(A.intersection(B))  # Same as above

# Difference: elements in A but not in B
print(A - B)  # {1, 2, 3}
print(A.difference(B))  # Same as above

# Symmetric difference: elements in either A or B but not both
print(A ^ B)  # {1, 2, 3, 6, 7, 8}
print(A.symmetric_difference(B))  # Same as above

# Update operations (modify the original set)
C = {1, 2, 3}
D = {3, 4, 5}

C.update(D)  # Union update (C now equals {1, 2, 3, 4, 5})
C.intersection_update(D)  # Intersection update (C now equals {3, 4, 5})
C.difference_update(D)  # Difference update (C now equals {})
C = {1, 2, 3}
C.symmetric_difference_update(D)  # Symmetric difference update (C now equals {1, 2, 4, 5})

Set Relationships and Comparisons

Sets can be compared to check for relationships like subsets, supersets, and equality.

# Subset and superset relationships
A = {1, 2, 3}
B = {1, 2, 3, 4, 5}
C = {1, 2, 3}
D = {6, 7, 8}

# Subset: all elements of A are in B
print(A.issubset(B))  # True
print(A <= B)  # True (subset operator)
print(A < B)  # True (proper subset: A ⊂ B, A ≠ B)

# Superset: all elements of B are in A
print(B.issuperset(A))  # True
print(B >= A)  # True (superset operator)
print(B > A)  # True (proper superset: B ⊃ A, B ≠ A)

# Equality: A and C have the same elements
print(A == C)  # True
print(A == B)  # False

# Disjoint: A and D have no elements in common
print(A.isdisjoint(D))  # True
print(A.isdisjoint(B))  # False (they share elements)

Practical Applications of Sets

Sets are incredibly useful for many common programming tasks, especially those involving unique values.

# Remove duplicates from a list
numbers = [1, 2, 2, 3, 4, 4, 5]
unique_numbers = list(set(numbers))  # [1, 2, 3, 4, 5] (order may vary)

# Membership testing (very efficient for large collections)
fruits = {"apple", "banana", "cherry"}
print("banana" in fruits)  # True
print("orange" in fruits)  # False

# Find common elements between collections
list1 = [1, 2, 3, 4]
list2 = [3, 4, 5, 6]
common = set(list1) & set(list2)  # {3, 4}

# Find unique elements in collections
only_in_list1 = set(list1) - set(list2)  # {1, 2}
only_in_list2 = set(list2) - set(list1)  # {5, 6}

# Count unique items
text = "Mississippi"
unique_chars = set(text)  # {'M', 'i', 's', 'p'}
print(len(unique_chars))  # 4

# Find all unique combinations
import itertools
dice1 = {1, 2, 3, 4, 5, 6}
dice2 = {1, 2, 3, 4, 5, 6}
combinations = {(x, y) for x in dice1 for y in dice2}  # 36 unique combinations

Comparing Dictionaries and Sets

Dictionaries and sets share some similarities but serve different purposes. Both use hash tables internally for fast lookups.

Feature Dictionaries Sets
Purpose Store key-value pairs Store unique elements
Syntax {key: value} {element}
Empty Collection {} set() (not {})
Lookup dict[key] or dict.get(key) element in set
Content Constraints Keys must be immutable, values can be any type Elements must be immutable
Common Operations Key lookup, updating values Union, intersection, difference
Time Complexity O(1) for lookup, insertion, deletion O(1) for lookup, insertion, deletion
Ordered Yes, since Python 3.7 No (unordered)

Practice Exercises

Try these exercises to solidify your understanding of dictionaries and sets:

  1. Create a function that counts the frequency of each word in a text and returns a dictionary.
  2. Create a function that finds all common elements between two lists using sets.
  3. Implement a simple phone book using a dictionary and allow users to add, search, and delete contacts.
  4. Use a set to filter duplicate entries from a list of email addresses.
  5. Create a nested dictionary to represent a simple JSON-like structure of user data.
Back to Cheat Sheet