Python provides powerful, flexible data structures that allow programmers to organize and store data in nested combinations. Two common examples of nested data structures in Python are lists of dictionaries and dictionaries of lists. Mastering these nested combinations unlocks the ability to model complex real-world data relationships in code.
This guide will provide Python developers with a comprehensive walkthrough of working with these nested data structures. Read on to learn about:
Table of Contents
Open Table of Contents
- Python Lists and Dictionaries Refresher
- Why Nested Data Structures?
- Constructing Nested Data Structures
- Accessing and Modifying Nested Data
- Iterating Through Nested Data Structures
- Built-in Functions for Nested Data Structures
- Best Practices for Nested Data Manipulations
- Applications of Nested Data Structures
- Conclusion
Python Lists and Dictionaries Refresher
Before diving into nested data structures, let’s quickly review the fundamentals of Python lists and dictionaries - the building blocks of nested collections.
Python Lists
A Python list is an ordered collection of objects enclosed in square brackets []
. Lists can contain elements of any data type, including other lists to form nested lists.
# Lists in Python
nums = [1, 2, 3]
fruits = ['apples', 'bananas', 'pears']
mixed = [1, 'two', 3.0, True]
nested = [[1,2], [3,4], [5,6]]
We can access, modify, add, and remove list elements using indexing and methods like append()
and remove()
.
# Accessing, modifying lists
print(fruits[1]) # 'bananas'
fruits[1] = 'grapes'
fruits.append('mangos')
fruits.remove('apples')
Python Dictionaries
A dictionary consists of key-value pairs enclosed in curly braces {}
. Dict keys must be unique within a dictionary while values can be duplicated.
# Dictionaries in Python
capitals = {'USA':'Washington DC', 'India':'New Delhi'}
user = {'name':'John', 'id':1234, 'email':'[email protected]'}
Access dictionary values by key, add new key-value pairs, modify values for existing keys, and remove keys using syntax like:
# Accessing, modifying dictionaries
print(capitals['India']) # 'New Delhi'
capitals['Russia'] = 'Moscow'
user['name'] = 'Jane'
del capitals['USA']
Now that we’ve reviewed the basics of Python’s fundamental collection types - lists and dictionaries - let’s look at combining them to model complex data.
Why Nested Data Structures?
Before we dive into the syntax, it’s important to understand why you may want to nest lists and dictionaries in Python. Why not keep everything separate in flat data structures?
Some key reasons to use nested data structures include:
-
Model hierarchical or grouped data relationships - Nesting allows logical grouping of related data. For example, storing multiple user profiles, each with their own individual data points, in a list of dicts.
-
Store mixed data types - Nesting allows combining different data types like lists with dicts to capture real-world data complexity.
-
Single data access - Nesting provides a way to efficiently access the full set of data for an element like a user or object profile through just that one outer key or index.
-
Maintain structure - Nesting maintains the relationships between data elements that would be lost in separate flat lists or dicts.
Now let’s look at ways to create these nested structures in Python code.
Constructing Nested Data Structures
Lists of Dictionaries
A common nested data structure in Python is a list of dicts. Each element in the outer list is a dictionary containing related key-value pairs.
For example, we can store a list of user profiles, where each user is represented by a dict with keys like “name”, “email”, “id” etc:
# List of dictionaries
users = [
{ 'name':'John', 'email':'[email protected]', 'id':1234 },
{ 'name':'Mary', 'email': '[email protected]', 'id': 4567 },
{ 'name':'Peter', 'email':'[email protected]', 'id':8910 }
]
We would access all the data for user John through users[0]
, Mary through users[1]
and so on.
Some key points about lists of dicts:
- The dictionaries in the list do not need to have the same keys. For example, some users may not have ‘id’ specified.
- The order of the dictionaries in the list matters - it is part of the data structure.
- Duplicate dicts can be stored in the list, unlike dict keys which must be unique.
Dictionaries of Lists
Another useful combination is a dictionary of lists. Here each dict key stores a list value.
For example, we can store a set of purchases by category:
# Dictionary of lists
purchases = {
'electronics': ['laptop', 'tv', 'smartphone'],
'toys': ['legos', 'puzzles', 'football'],
'clothing': ['shirts', 'pants', 'dresses']
}
Some common uses for dictionaries of lists:
- Store categorized or grouped data for easy access, like products by category.
- Maintain ordered records of field values, like transaction history for each customer ID.
- Create sparse array-like structures by using integers as keys.
These examples demonstrate the flexibility of nested data structures for modeling real-world data relationships in code.
Accessing and Modifying Nested Data
Now that we can construct lists of dicts and dicts of lists, let’s look at techniques to access and modify elements within these nested structures.
Accessing Nested Elements
To access nested data, we chain indexing or key operations together to “drill down” into the data structure.
For example, to access user Mary’s email from our list of user dicts, we would use:
# Access nested elements
users[1]['email'] # '[email protected]'
We index the outer list first with [1]
, then the dict with [‘email’] to get the value.
Similarly, to get the first purchase in the ‘toys’ category from our dict of lists:
purchases['toys'][0] # 'legos'
We can continue chaining indices and keys arbitrarily deep to access nested elements in complex data structures.
Modifying Nested Elements
To modify nested data, we combine indexing/key operations with value assignment.
For example, to change Mary’s email address:
users[1]['email'] = '[email protected]'
And to add a new clothing purchase:
purchases['clothing'].append('scarf')
All the standard list methods like .append()
and .remove()
, along with dict operations work on nested structures in this way.
One important note is that care must be taken when modifying mutable values like lists or dicts. For example:
my_list = [ [1,2], [3,4] ]
sub_list = my_list[0] # Points to same object
sub_list.append(5)
print(my_list) # [[1,2,5], [3,4]]
Here sub_list
points to the same list object as my_list[0]
. Changes to one will affect the other. To avoid this, make a copy first before modifying:
sub_list = my_list[0][:] # Copy
sub_list.append(5)
Iterating Through Nested Data Structures
Iterating through nested lists and dictionaries requires some special techniques compared to flat data structures.
Iterating Through Lists of Dicts
To process a list of dicts, we:
- Iterate through the outer list
- Access each inner dict
- Work with the dict’s keys/values
For example:
for user in users:
print(user['name']) # Print each user's name
user['logged_in'] = True # Mark them logged in
We can also directly access both the index and dict together:
for i, user in enumerate(users):
print(f"User {i} is {user['name']}")
Iterating Through Dicts of Lists
For dictionaries of lists, we:
- Loop through the dictionary’s keys
- Access each inner list value
- Iterate through the list contents
For example:
for category, purchases in purchases.items():
print(f"{category}: {purchases}")
for purchase in purchases:
print(purchase)
This allows us to access both the outer key and inner list values during iteration.
Using Nested Loops
For additional processing, we can add nested for
loops to iterate through multiple levels:
for user in users:
for key, value in user.items():
print(f"{key}: {value}")
The inner loop iterates through each dict within the outer list.
Nested loops allow performing any arbitrary processing on the different levels of nested data structures.
Built-in Functions for Nested Data Structures
Python provides several built-in functions that are useful when working with nested lists and dictionaries:
len()
- Returns total nested lengthsum()
- Sums all numeric nested elementsany()/all()
- Check truth value of nested elementsmin()/max()
- Determine min/max nested elementsorted()
- Sort by nested values
For example:
# Length of outer list
num_users = len(users)
# Sum all purchases
total_purchases = sum(len(v) for v in purchases.values())
# Sort users by id
sorted_users = sorted(users, key=lambda user: user['id'])
These built-ins make it easy to work with nested data structures without extensive looping yourself.
Best Practices for Nested Data Manipulations
When working with nested data structures in Python, keep these best practices in mind:
-
Use descriptive, consistent naming conventions - Name outer and inner collections and elements clearly. Follow conventions like
users_list
,user_dict
,user_purchases_dict
etc. -
Comment nested logic - Use comments to document the nested structures and explain indexing/key logic to aid future maintenance.
-
Avoid deep nesting - Design to minimize excessive chaining that makes the code hard to read and maintain. Reorganize into helper methods or flat structures when it gets unwieldy.
-
Check for existence first - Check if a key or index exists before accessing nested values to avoid errors.
-
Take advantage of built-ins - Use built-in functions and methods for nested structures whenever possible instead of complex manual loops.
Mastering these nested data structure best practices will lead to more readable, maintainable Python codebases.
Applications of Nested Data Structures
Let’s briefly highlight some real-world examples of where nested Python data structures are commonly used:
-
User management - Lists of dicts for user profiles, with history/activity as lists of interactions.
-
Product catalog - Dictionary of lists for product categories, each containing individual product dicts.
-
Analytics data - Lists of events like pageviews with nested dicts of metadata like browser.
-
Scientific data - Grids modeled as dict of lists with tuple keys for coordinates.
-
Graph representations - Nodes and edges stored as dictionaries of neighbors represented by lists.
The options are endless for modeling domain data in nested Python data structures.
Conclusion
This guide covered the fundamentals of working with nested lists of dictionaries and dictionaries of lists in Python. We explored:
- Reasons for using nested data structures like lists of dicts and dicts of lists.
- Techniques for constructing, accessing, and modifying nested data.
- Iteration approaches for nested loops and built-in functions.
- Best practices for readable and maintainable nested data manipulations.
- Real-world examples of using these structures for modeling complex data relationships.
The key takeaways are:
- Use lists of dicts to store related records like object or user profiles.
- Use dicts of lists to collect grouped information like categories.
- Access values through chained indexing/keys and assign to modify.
- Iterate with nested loops and leverage built-in functions.
- Follow naming conventions and commenting best practices.
With this knowledge, you are now equipped to harness the power of nested data structures in your own Python programs!