[Help Requested] Dictionary Comprehension for Python Code Challenge

This is from one of the Python Code Challenges in the Intro to Programming unit of the Computer Science pathway. Link to the page. The goal is to take a dictionary that has last names as the key and a list of corresponding first names as each value, and return a new dictionary with the last initial as the key and the number of people with that same last initial as the value.

Here’s my code.

def count_first_letter(names):
    return {key[0]: len([first_name for first_name in names.values() if first_name[0] == key[0]]) for key in names}

print(count_first_letter({"Stark": ["Ned", "Robb", "Sansa"], "Snow" : ["Jon"], "Lannister": ["Jaime", "Cersei", "Tywin"]}))

The print statement should return {‘S’: 4, ‘L’: 3}, but instead returns {‘S’: 0, ‘L’: 0}.

I spent a good amount of time trying to figure this out before posting this. I know there are other ways to solve this challenge, but I’m hoping learn where I’m going wrong with the comprehension. Thanks in advance for any help.

First rule of problem solving is start with the simplest steps and get the logic to work. The streamlining and refinement can come later.

Study the problem, study the data, toss around ideas of how the data can be used to solve the problem.

Say for instance we start by composing a dictionary of just the first letters. Ignore that this might look like an extra step, it is, but it has a simpler objective.

names = {"Stark": ["Ned", "Robb", "Sansa"], "Snow" : ["Jon"], "Lannister": ["Jaime", "Cersei", "Tywin"]}
initials = {}
for key in names.keys():
    initials[key[0]] = 0
>>> initials
{'S': 0, 'L': 0}

Okay, we’ll leave you to come up with a basic algorithm to cycle through the dictionary again, and accumulate the counts in the initials dictionary. Please let us know how you are doing, or if you are still having problems. Remember to keep it simple and get something to work before trying to refine it. We’re more interested in your algorithm than fancy one-liner code, for this reason.

1 Like

Aside and Extra Study

As to the comprehension, we need a mechanism to get around the two step process described above. To be honest, I’d rather stick with that code than venture further into the expressive world, but for the sake of tinkering and learning, well, you know where this is going…

>>> d = {}
>>> a = [1,2,3,4]
>>> b = [5,6,7]
>>> d['s'] = d.get('s', 0) + len(a)
>>> d
{'s': 4}
>>> d['s'] = d.get('s', 0) + len(b)
>>> d
{'s': 7}

What can you garner from that?

I’ve gone and jumped ahead, hope you won’t mind.

>>> names = {"Stark": ["Ned", "Robb", "Sansa"], "Snow" : ["Jon"], "Lannister": ["Jaime", "Cersei", "Tywin"]}
>>> d = {}
>>> for k, v in names.items():
...     d[k[0]] = d.get(k[0], 0) + len(v)
>>> d
{'S': 4, 'L': 3}

How this figures into a comprehension is going to take some, well, figuring. I’ll leave it up to you to take it to the next dimension: Expression.

Now to go completely off topic, the beauty of the interactive shell is that we can test our hypothesis immediately. Just as long as it is a complete expression the shell will give us a response, else raise an exception. Debug as you invent code and there will undoubtedly be less to debug in you production code. Mind, the process never ceases if we’re always trying to break our code, right?

1 Like

I’m sorry for my extremely late reply. Life has a way of throwing unexpected curveballs.
I appreciate your two thorough replies. I’m definitely going to take the advice you gave to start with the simplest steps to solve the problem before attempting to streamline it.

Honestly, I didn’t understand much of the first bit of code in the second reply. That may direct me further to focus on more basic solutions for now.

As for the second bit of code in that response, I really had to break it down in my head because I’m not very familiar with the get() method. The for loop iterates through each key/value pair, before the equal sign it creates a key for the first letter of every key in the names dictionary. But after the equal sign, I didn’t understand because ‘d’ has nothing in it, and I don’t see where the value retrieved by “d.get(k[0], 0)” is turned into an integer.

I’m sure as I continue learning python this will become clear eventually, so if you don’t respond that’s okay, I know my response comes pretty late. But I appreciate your first two responses regardless.

1 Like

It is turned into an integer on its first encounter with k[0] which does not yet exist in the dictionary, but by the assignment, now does, and is assigned the value, 0 plus the length of the corresponding value (an array).

Notice that once we add the key, it is then retrievable by .get() so its value gets augmented by the next list length. We have effectively created a histogram, which may also require some reading as you progress.

.get() permits us to query an object for any key, whether it exists or not. If it exists, its value is what is returned. If it does not exist, the second positional argument, in this case zero, is returned. Notice that there is no KeyError raised in this method. I’ll leave you to read up some more and adopt its usage as you move towards harder challenges. It will prove useful once you are comfortable with it and know how to use it effectively.

1 Like

.get() Does seem very useful, and I’ll add it to my toolbag.

I ran the code in pieces and realized another misunderstanding I had. In my mind, I was treating the for loop like a nested loop, where it would iterate through every first name, and every last name, but in reality the code just loops through three items, each key-value pair. And that was why I still didn’t understand why it was returning an integer, even after your explanation, but now I do, thank for taking the time to respond.

1 Like