Welcome to yet another How to Python article. Today, we’ll be looking at looping over dictionaries which appears to be a hot topic—at least by an organic standpoint.
As it turns out, there are few ways to get it done. First, we could loop over the keys directly: for key in dictionary
. Alternatively, we might only need to loop over the values: for value in dictionary.values()
. That said, most folks probably need to be able to do both at the same time: for key, value in dictionary.items()
.
If you’re interested in learning more about these solutions, the remainder of this article will take some time to help you out. At the very least, I’d love it if you completed the challenge below.
Table of Contents
Problem Description
In the past, we talked about writing loops generally. Of course, when it comes to working with common data structures like lists and tuples in Python, looping over them is a breeze:
data = [1, 5, 4, 3] for num in data: pass # Do something!
However, once we start talking about more complicated data structures like dictionaries, iteration becomes a bit more complicated. For example, here’s a dictionary that we’ll be using throughout this article:
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 }
If we want to loop over this, what order can we expect it to be in? And, does the concept of ordering even make sense? Likewise, what do the elements look like during iteration?
Before we dive in, I think it’s important to address a couple of these questions right out of the gate. First, dictionaries have a temporal ordering—at least since Python 3.7—which means that items are sorted by the order in which they were added.
That said, element question is a bit more complicated, so we’ll take the rest of this article to answer it.
Solutions
At this point, let’s go ahead and start talking about how to actually iterate over a dictionary. As it turns out, there are three main ways to do it, and it all depends on our needs. That said, we’ll start by looking at keys and values separately and finish up with a way to loop over both at the same time.
Iterating Over Dictionary Keys
If we were to use the same strategy on the dictionary as we did on the list above, we may find ourselves a little confused (as one individual does in this StackOverflow question):
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } for player in players: print(player) # Prints the following: # Crosby # Malkin # Letang
As we can see, the player
variable seems to store each key. To make use of this information, we’ll need to use these keys to access our values:
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } for player in players: print(f'{player}\'s number is {players[player]}') # Prints the following: # Crosby's number is 87 # Malkin's number is 71 # Letang's number is 58
Notice how we used the player
variable to access our players
dictionary. On each pass, we picked up a new player and retrieved their number directly.
Iterating Over Dictionary Values
If for some reason, we don’t need keys, Python gives us the option to iterate over values instead:
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } for number in players.values(): print(number) # Prints the following: # 87 # 71 # 58
Here, we’ve used the values()
method of dictionaries to retrieve an iterable dict_values
object. That way, we’re able to loop over the values and work with them directly.
If for some reason, we needed to retrieve a key associated with a value, we can do that. In fact, I have an entire separate article on reverse dictionary lookups. That said, there’s probably an even better option coming up.
Iterating Over Dictionary Keys and Values
Up to this point, we’ve been iterating over the keys and values separately. As it turns out, there are ways to iterate over both at the same time:
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } for player, number in players.items(): print(f'{player}\'s number is {number}') # Prints the following: # Crosby's number is 87 # Malkin's number is 71 # Letang's number is 58
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } for player, number in players.iteritems(): print(f'{player}\'s number is {number}') # Prints the following: # Crosby's number is 87 # Malkin's number is 71 # Letang's number is 58
Assuming we’re using the latest version of Python, we can iterate over both keys and values at the same time using the items()
method. Essentially, this method packages each key and value as a tuple which can be unpacked using the iterable unpacking syntax (aka destructuring for you JavaScript folks).
If you’re interested in learning more about iterable unpacking, I’ve included an example in a previous article in this series. Likewise, the feature also made an appearance on my list of the coolest programming language features.
At any rate, let’s go ahead and compare these three options in terms of performance.
Performance
To compare these solutions, we’ll need to come up with a consistent scenario. Here, we’ll assume that we’ll need both the key and the value. Obviously, this gives the advantage to the items()
solution, so the rationale is that this is probably the most common scenario.
At any rate, we’ll be using the timeit
library which runs code snippets as strings. If you’re interested in learning more about this testing process, check out my article on performance testing in Python. Otherwise, here are the strings:
setup = """ players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } """ keys_solution = """ for player in players: player_info = f\"{player}'s number is {players[player]}\" """ values_solution = """ for number in players.values(): player = next(player for player in players.keys() if players[player] == number) player_info = f\"{player}'s number is {players[player]}\" """ items_solution = """ for player, number in players.items(): player_info = f\"{player}'s number is {number}\" """
Now that we have our strings, we can begin our testing:
>>> import timeit >>> min(timeit.repeat(setup=setup, stmt=keys_solution)) 0.6103567999998631 >>> min(timeit.repeat(setup=setup, stmt=values_solution)) 2.5487096000001657 >>> min(timeit.repeat(setup=setup, stmt=items_solution)) 0.6782263000000057
Clearly, this is a bit surprising because of the extra lookup required for the keys solution. I would have assumed that the items()
solution would have won out. Naturally, I couldn’t help but test this problem with a larger data set, so I expanded our players dictionary:
setup = """ players = { "Crosby": 87, "Malkin": 71, "Letang": 58, "Guentzel": 59, "Aston-Reese": 46, "Blueger": 53, "Johnson": 3, "Jarry": 35, "Murray": 30, "Marino": 6, "Rust": 17, "Sheary": 43, "Zucker": 16 } """
Now that we have about 4 times the players, let’s check back in on our algorithms:
>>> min(timeit.repeat(setup=setup, stmt=keys_solution)) 2.6091060999997353 >>> min(timeit.repeat(setup=setup, stmt=items_solution)) 2.5544105999997555
This time around I didn’t bother testing the values solution because, well, it’s already quite slow. That said, it looks like the items solution is already starting to edge out the keys solution. I wonder what this test would look like with even more data. Feel free to give it a try and let me know!
For reference, I tested all this code on Python 3.7.3 in IDLE using Windows 10.
Challenge
Now that we know how to iterate over a dictionary, I’m wondering if we could take some of this knowledge to the next level. For instance, what if we had the following data structure:
health_data = ( ( "05-01-2020", # Date 180.5, # Weight 8043 # Steps ) ... )
In other words, we have some health data in the form of a tuple of tuples. Each inner tuple has three data items: date, weights, and steps. Is there some way we could effectively process each row of data using what we’ve learned today?
As always, I’ll share my answer on Twitter using #RenegadePython:
Are there other ways to do this? Why not share your own solution using the same hashtag? If I see it, I’ll give it a share!
A Little Recap
As always, here’s the list of solutions:
players = { "Crosby": 87, "Malkin": 71, "Letang": 58 } # Loops over just the keys for player in players: pass # Loops over just the values for number in players.values(): pass # Loops over both keys and values (Python 3) for player, number in players.items(): pass # Loops over both keys and values (Python 2) for player, number in players.iteritems(): pass
Beyond that, we’re done here! If you’re interested in sticking around, here are few other dictionary articles:
- How to Perform a Reverse Dictionary Lookup in Python
- How to Merge Two Dictionaries in Python
- How to Sort a List of Dictionaries in Python
Likewise, I’d love it if you checked out my article on ways to grow The Renegade Coder. In it, you’ll learn how to hop on my mailing list or join me on Patreon.
Otherwise, here are a few Python resources for the road on Amazon (ad):
- Creative Coding in Python: 30+ Programming Projects in Art, Games, and More
- Mission Python: Code a Space Adventure Game!
With that said, thanks for hanging out! I appreciate your time.
Recent Code Posts
Python has a cool feature that allows you to overload the operators. Let's talk about what that means and how you might use it!
This week, we're hitting another beginner topic: the assignment operator. While the idea is simple, the concept is rich in related ideas like scope, iterable unpacking, and augmented assignment.