โณ Loading Python Engine...

๐Ÿ“Š Day 07 : Dictionaries

๐ŸŽฏ Enterprise Objective

Dictionaries are the most critical data structure in modern programming. They map keys to values with lightning-fast O(1) lookups and are the exact equivalent of JSON objects. Today we master dictionary manipulation, safe extraction, functional comprehensions, and deep nested hierarchies. Mastery of dictionaries is non-negotiable for working with APIs and DataFrames.

๐Ÿ“‹ Strategic Overview

#TopicKey ConceptCore Use Case
1Creation{'k': 'v'}, dict(k='v')JSON modeling
2Access & Safety.get(k, default)Avoid KeyError crashes
3Views.keys(), .values(), .items()Fast dynamic views
4Iterationfor k, v in d.items():Data processing
5Comprehensions{k: v for k, v in data}Transformation
6Merging`d1 \d2, .update()`Aggregating data
7Nested DictsDeep hierarchical dataAPI Payloads

1. Dictionary Creation & Basics : Key-Value Mapping

๐Ÿ” What is it?

A dictionary is a mutable, unordered collection of key-value pairs. It is Python's most powerful data structure, serving as the foundation for JSON parsing, DataFrames, and fast lookups. Keys must be hashable (strings, numbers, tuples), while values can be anything.

Creation MethodExampleNote
Braces{'a': 1, 'b': 2}Standard literal
dict() constructordict(a=1, b=2)Clean for string keys
From pairsdict([('a',1), ('b',2)])Converting lists of tuples
fromkeys()dict.fromkeys(['a','b'], 0)Initialize with defaults
Empty dict{} or dict()Empty dictionary

๐Ÿ’ผ Why Data Analysts Care

โ€ข JSON Data: Dictionaries are the Python equivalent of JSON objects

โ€ข Fast Lookups: Looking up a value by its key is an O(1) operation

โ€ข Data Modeling: Representing real-world entities (e.g., a user profile)

โš ๏ธ Mutable Keys

You cannot use a list or dictionary as a dictionary key because they are unhashable (mutable). You will get a TypeError: unhashable type: 'list'. Use tuples instead.

๐Ÿง  Pro Tip

Since Python 3.7, dictionaries maintain insertion order. But never rely on this for logic; if order matters explicitly, document it or use collections.OrderedDict for backward compatibility.

In [ ]:

๐Ÿงช Concept Checks: Dictionary Creation

Q1. Create a dictionary representing a book with keys: title, author, year. Print the dictionary and its type.

In [ ]:

Q2. Create a dictionary using the dict() constructor with kwargs for x=10, y=20, z=30. Print it.

In [ ]:

Q3. Use dict.fromkeys() to initialize a dictionary with keys ["mon", "tue", "wed"] and default value None. Print it.

In [ ]:

Q4. Try to create a dictionary with a list as a key: {[1, 2]: "value"}. Catch the TypeError and print the error message.

In [ ]:

Q5. Fix the previous code by using a tuple instead of a list for the key. Print the valid dictionary.

In [ ]:

2. Accessing & Modifying Data : Safe Lookups & Updates

๐Ÿ” What is it?

You can access values using dict[key], but if the key doesn't exist, it raises a KeyError. To safely access data, use the .get(key, default) method. To add or modify, simply assign dict[key] = value.

OperationSyntaxBehavior
Unsafe Accessd['key']Returns value or raises KeyError
Safe Accessd.get('key')Returns value or None (no error)
Safe w/ Defaultd.get('key', 0)Returns value or 0
Add / Modifyd['key'] = valCreates new or overwrites existing
Deletedel d['key']Removes key-value pair
Pop & Returnd.pop('key')Removes and returns the value

๐Ÿ’ผ Why Data Analysts Care

โ€ข API Parsing: APIs often have missing fields. Use .get() to avoid crashing your pipeline

โ€ข Counter Patterns: d[key] = d.get(key, 0) + 1 is the standard way to count frequencies

โ€ข Configuration Defaults: port = config.get('port', 80) applies safe default values

โš ๏ธ KeyError Crashes

data['missing_key'] is the #1 cause of crashes in data pipelines. Always use .get() when parsing external data.
In [ ]:

๐Ÿงช Concept Checks: Access & Modification

Q1. Given user = {"name": "Alice"}, add a new key "age" with value 28. Then update "name" to "Alice Smith". Print the dict.

In [ ]:

Q2. Demonstrate KeyError: try to access user["city"] in a try/except block. Print the error message.

In [ ]:

Q3. Use .get() to safely access "city" with a default value of "Unknown". Print the result.

In [ ]:

Q4. Write the standard counting pattern: given counts = {"a": 1}, increment "b" by 1 using counts["b"] = counts.get("b", 0) + 1. Print the dict.

In [ ]:

Q5. Use .pop() to remove "name" from user and save it to a variable. Print the popped value and the remaining dictionary.

In [ ]:

3. Dictionary Views : Keys, Values, and Items

๐Ÿ” What is it?

Dictionaries provide dynamic view objects through three methods: .keys(), .values(), and .items(). These views update automatically when the dictionary changes and are fast and memory-efficient.

MethodReturns View OfExample Use
.keys()All keysChecking membership: 'age' in d.keys()
.values()All valuesStatistics: sum(d.values())
.items()(key, value) tuplesIteration: for k, v in d.items():

๐Ÿ’ผ Why Data Analysts Care

โ€ข Column Checks: Check if expected columns exist in a data record using .keys()

โ€ข Data Extraction: Extract all metric values for analysis using .values()

โ€ข Transformation: Iterate over .items() to apply a function to all values

โš ๏ธ Views are not Lists

Views don't support indexing. d.keys()[0] raises a TypeError. If you need a list, explicitly convert it: list(d.keys()).

In [ ]:

๐Ÿงช Concept Checks: Dictionary Views

Q1. Given data = {"a": 10, "b": 20, "c": 30}, extract the values and calculate their sum. Print the sum.

In [ ]:

Q2. Check if the key "d" exists in data.keys(). Print the boolean result.

In [ ]:

Q3. Convert data.items() to a list of tuples. Print the list.

In [ ]:

Q4. Prove views are dynamic: assign view = data.values(), add "d": 40 to data, then print view. Note how it updated.

In [ ]:

Q5. Try to access data.keys()[0]. Catch the TypeError and print the error message. Show the correct way by converting to a list first.

In [ ]:

4. Iteration Patterns : Looping over Dictionaries

๐Ÿ” What is it?

Iterating directly over a dictionary loops over its keys. To loop over both keys and values, use .items(). This is the most common and Pythonic way to process dictionary data.

# โŒ Bad: Looping keys to get values
for k in d:
    v = d[k]  # Inefficient lookup

# โœ… Good: Unpacking items directly
for k, v in d.items():
    print(k, v)

๐Ÿ’ผ Why Data Analysts Care

โ€ข Formatting Output: Printing structured reports of metrics or configuration

โ€ข Data Cleaning: Iterating over items to clean strings or convert types

โ€ข Filtering: Looping to build a new dictionary with specific key-value pairs

๐Ÿง  Pro Tip

You cannot add or remove keys from a dictionary while iterating over it. It will raise a RuntimeError: dictionary changed size during iteration. If you must modify keys, iterate over a copy or build a new dictionary.

In [ ]:

๐Ÿงช Concept Checks: Iteration

Q1. Iterate over prices = {"apple": 1.2, "banana": 0.5} and print just the keys using a standard for loop.

In [ ]:

Q2. Iterate over prices.values() and print each value formatted as currency (e.g., $1.20).

In [ ]:

Q3. Use .items() to iterate over prices and print "[Item] costs $[Price]".

In [ ]:

Q4. Write a loop over .items() that creates a new dictionary expensive containing only items costing more than $1.00. Print it.

In [ ]:

Q5. Demonstrate the RuntimeError: try to del d[k] while iterating over d = {"a":1, "b":2}. Catch the error and print it.

In [ ]:

5. Dictionary Comprehensions : Functional Dictionary Creation

๐Ÿ” What is it?

Just like list comprehensions, dictionary comprehensions provide a concise way to create dictionaries from iterables. The syntax is {key: value for item in iterable if condition}.

# Square numbers mapping
squares = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

๐Ÿ’ผ Why Data Analysts Care

โ€ข Data Transformation: Convert a list of records into a dictionary keyed by ID for fast lookups

โ€ข Filtering Dictionaries: {k: v for k, v in data.items() if v > 0} โ€” clean negative values

โ€ข Key Mapping: Mapping original column names to new clean column names

๐Ÿง  Pro Tip

A very common pattern for fast lookups: lookup = {row['id']: row for row in dataset}. This converts an O(n) search into an O(1) dictionary lookup.

In [ ]:

๐Ÿงช Concept Checks: Dict Comprehensions

Q1. Create a dictionary comprehension that maps numbers 1-5 to their cubes (x**3). Print it.

In [ ]:

Q2. Given keys = ["a", "b", "c"] and values = [1, 2, 3], use zip() in a comprehension to create a dictionary. Print it.

In [ ]:

Q3. Filter data = {"x": -5, "y": 10, "z": -2, "w": 15} to keep only positive values using a comprehension. Print the result.

In [ ]:

Q4. Swap keys and values in codes = {"NY": "New York", "CA": "California"} using a comprehension. Print the result.

In [ ]:

Q5. Create a dictionary mapping each character in "Data Science" to its uppercase version. Notice what happens to duplicate characters. Print it.

In [ ]:

6. Merging & Updating : Combining Dictionaries

๐Ÿ” What is it?

Python provides multiple ways to combine dictionaries. dict.update() modifies in-place. Python 3.9+ introduced the merge operator | which creates a new dictionary.

OperationSyntaxEffect
Update in-placed1.update(d2)d1 is modified. d2 keys overwrite d1. Returns None.
Merge operator (3.9+)`d3 = d1 \d2`Creates new dict d3. d2 overwrites d1.
Unpackingd3 = {d1, d2}Creates new dict. Pre-3.9 equivalent of `\`.

๐Ÿ’ผ Why Data Analysts Care

โ€ข Configuration Management: Merge default_config with user_config (user overrides default)

โ€ข Aggregating Data: Combining parsed JSON objects from multiple API endpoints

โš ๏ธ Right Wins

When merging, if the same key exists in both dictionaries, the value from the dictionary on the right side (or passed to .update()) wins.

In [ ]:

๐Ÿงช Concept Checks: Merging & Updating

Q1. Merge d1 = {"a": 1, "b": 2} and d2 = {"b": 99, "c": 3} using .update(). Note the value of "b". Print d1.

In [ ]:

Q2. Merge the same dictionaries using the | operator into a new dict d3. Print d3.

In [ ]:

Q3. Merge three dictionaries d1, d2, d3 = {"d": 4} using kwargs unpacking {d1, d2, **d3}. Print the result.

In [ ]:

Q4. Try to merge a dictionary and a list {"a": 1} | ["b", 2]. Catch the TypeError and print it.

In [ ]:

Q5. Write a function apply_defaults(config, defaults) that returns a new config where config values override defaults. Test it.

In [ ]:

7. Nested Dictionaries : JSON & Deep Data

๐Ÿ” What is it?

A dictionary can contain other dictionaries (or lists, sets, etc.) as values. This is essential for working with hierarchical data like JSON or NoSQL document databases.

users = {
    'u1': {'name': 'Alice', 'role': 'Admin'},
    'u2': {'name': 'Bob', 'role': 'User'}
}
# Access nested data
alice_role = users['u1']['role']

๐Ÿ’ผ Why Data Analysts Care

โ€ข API Parsing: Traversing complex JSON responses from web services

โ€ข Document Storage: Representing MongoDB documents in memory

โ€ข Configuration Trees: Hierarchical settings (e.g., config['database']['host'])

โš ๏ธ Deep Missing Keys

Accessing data['user']['address']['zip'] will crash if any key in the chain is missing. Use chained .get() or handle exceptions.

In [ ]:

๐Ÿงช Concept Checks: Nested Dictionaries

Q1. Create a nested dictionary representing a company with two departments ("Sales", "Engineering"), each containing a list of employee names. Print it.

In [ ]:

Q2. Access and print the first employee in the "Engineering" department from the dictionary above.

In [ ]:

Q3. Add a new department "HR" with an empty list of employees to the company dictionary. Print the updated dict.

In [ ]:

Q4. Safely extract "city" from data = {"user": {"id": 1}} using chained .get() to avoid KeyError. Print the default "Unknown".

In [ ]:

Q5. Use json.dumps(dict, indent=4) to pretty-print your company dictionary. Print the result.

In [ ]:

๐Ÿ› ๏ธ Professional Practice Tasks

Theory is useless without muscle memory. Complete these tasks to solidify your understanding.

Task 1 (Frequency Counter): Write a function count_words(text) that takes a string, splits it into words (lowercase), and returns a dictionary of word frequencies. Handle punctuation if you can. Test with a short paragraph.

In [ ]:

Task 2 (Dict Inverter): Write a function invert_dict(d) that swaps keys and values. Since multiple keys might have the same value, the new dictionary should map value -> list_of_keys. Test with {'a':1, 'b':2, 'c':1}.

In [ ]:

Task 3 (JSON Path Extractor): Write a function extract_path(data, path_list) that safely navigates a nested dict. extract_path(data, ['user', 'address', 'city']). Return None if any key is missing.

In [ ]:

Task 4 (Data Grouper): Given a list of dictionaries [{'dept': 'HR', 'name': 'Alice'}, {'dept': 'IT', 'name': 'Bob'}, {'dept': 'HR', 'name': 'Charlie'}], write code to group them by department: {'HR': ['Alice', 'Charlie'], 'IT': ['Bob']}.

In [ ]:

Task 5 (Config Merger): Write a function deep_merge(d1, d2) that merges two dictionaries. If a key contains a nested dictionary in both, recursively merge them. Otherwise, d2 overwrites d1.

In [ ]:

๐Ÿ’ป Pure Coding Interview Questions

Q1.

Write a function two_sum(nums, target) using a dictionary to find the pair of indices in O(n) time.

In [ ]:

Q2.

Write a function is_isomorphic(s, t) that checks if two strings are isomorphic using a dictionary mapping.

In [ ]:

Q3.

Write a function lru_cache_simulation(queries, capacity) using collections.OrderedDict to track recently used keys.

In [ ]:

Q4.

Write a function group_anagrams(strs) using a dictionary where the key is the sorted string and the value is a list of anagrams.

In [ ]:

Q5.

Write a function first_unique_char(s) using a dictionary to count frequencies, then a second pass to find the first with count 1.

In [ ]:

Q6.

Implement a Trie (Prefix Tree) data structure using nested dictionaries. Include insert and search methods.

In [ ]:

Q7.

Write a function find_itinerary(tickets) given a list of (source, dest) tuples, reconstruct the full itinerary using a dict.

In [ ]:

Q8.

Write a function contains_duplicate_nearby(nums, k) checking if duplicate values exist within distance k using a dict.

In [ ]:

Q9.

Write a function longest_consecutive_sequence(nums) using a dictionary/set for O(n) time complexity.

In [ ]:

Q10.

Write a function sort_characters_by_frequency(s) returning a string with characters sorted by decreasing frequency.

In [ ]:

Q11.

Write a function subarray_sum(nums, k) that finds the total number of continuous subarrays whose sum equals k. Use a dict to store prefix sums.

In [ ]:

Q12.

Write a function roman_to_int(s) using a dictionary to map Roman numerals to integers.

In [ ]:

Q13.

Write a function majority_element(nums) using a dictionary to count frequencies and find the element appearing > n/2 times.

In [ ]:

Q14.

Implement an LFUCache (Least Frequently Used) using combinations of dictionaries for O(1) operations.

In [ ]:

Q15.

Write a function design_hashmap() from scratch using a list of lists (chaining) without using the built-in dictionary.

In [ ]:

Q16.

Write a function word_pattern(pattern, s) using dictionaries to check a bijective mapping between a pattern and words.

In [ ]:

Q17.

Write a function find_all_anagrams_in_string(s, p) using sliding window and dictionaries to count characters.

In [ ]:

Q18.

Write a function flatten_nested_dict(d) that flattens {'a': {'b': 1}} into {'a.b': 1}.

In [ ]:

Q19.

Write a function dict_difference(d1, d2) returning keys added, removed, and modified between two dictionaries.

In [ ]:

Q20.

Write a function top_k_frequent_elements(nums, k) using a dictionary and sorting/heap.

In [ ]:

Q21.

Explain the internal implementation of a Python dictionary (Hash Table, open addressing, PyDictObject).

In [ ]:

Q22.

Write a function check_valid_sudoku(board) using dictionaries/sets to track seen numbers in rows, columns, and 3x3 grids.

In [ ]:

Q23.

Write a function minimum_window_substring(s, t) using a dictionary to track character counts in a sliding window.

In [ ]:

Q24.

Write a function find_duplicate_file(paths) grouping identical file contents (using content hash as dict key).

In [ ]:

Q25.

Implement defaultdict behavior manually by subclassing dict and overriding the missing method.

In [ ]:

๐Ÿ“Š Day 7 Executive Summary

#TopicKey TakeawayProfessional Application
1CreationKey-Value pairs, hashable keys, O(1) lookupJSON modeling, caching
2Access/ModifyUse .get(key, default) for safetyRobust API parsing
3Viewskeys(), values(), items() are dynamicData extraction
4Iterationfor k, v in d.items(): is standardData transformation loops
5Comprehensions{k: v for ...} creates dicts cleanlyFast filtering & mapping
6Merging`d1 \d2 or .update()`Combining configurations
7Nested DictsDicts inside dicts represent JSONNoSQL, complex payloads

โœ… Instructor's End-of-Day Checklist

โ€ข [ ] I understand why dictionary keys must be immutable (hashable).

โ€ข [ ] I know to use .get() to safely extract data without crashing.

โ€ข [ ] I can iterate efficiently using .items().

โ€ข [ ] I can create dictionaries quickly using dictionary comprehensions.

โ€ข [ ] I know how to merge two dictionaries using the | operator or .update().

โ€ข [ ] I understand how to navigate nested dictionaries (JSON objects).

โ€ข [ ] I have completed all 5 practice tasks.

โ€ข [ ] I have reviewed all 25 interview questions.