Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Python Collections (2016, retired 2019) Dictionaries Teacher Stats

Anthony Lopez
Anthony Lopez
963 Points

Which teacher has the most courses?

Hey guys,

I keep getting this question wrong. I have a suspicion I'm not using the max() function right. It's supposed to call the largest item in an iterable but I keep getting the wrong answer.

I also wrote another function just trying to return the max of the dictionary but that is also incorrect.

  1. Can you please help me to understand what is actually happening when I use the max function.

  2. Am I using my if statement correctly? Should I be able to return the course associated with the largest course list?

Thanks!

teachers.py
# The dictionary will look something like:
# {'Andrew Chalkley': ['jQuery Basics', 'Node.js Basics'],
#  'Kenneth Love': ['Python Basics', 'Python Collections']}
#   
# Each key will be a Teacher and the value will be a list of courses.
#
# Your code goes below here.

def num_teachers(sing_arg):
    return len(sing_arg)

def num_courses(sing_arg):
    value = 0
    for course in sing_arg.values():
        value += len(course)
    return value

def courses(sing_arg):
    new_list = []
    for course in sing_arg.values():
        new_list.extend(course)  
    return new_list


# return the name of the teacher with the most courses
def most_courses(sing_arg):
    return max(sing_arg)


def most_courses(sing_arg):
    new_list = []
    for course in sing_arg.keys():
        new_list.append(sing_arg[course])
        if sing_arg[course] == max(new_list):
            return course

2 Answers

Kyle Knapp
Kyle Knapp
21,526 Points

Let's walk through your current function.

def most_courses(sing_arg):
    new_list = []
    for course in sing_arg.keys():
        new_list.append(sing_arg[course])
        if sing_arg[course] == max(new_list):
            return course

First you assign new_list to an empty list. Then you append each list of courses in sing_arg to new_list. At this point, new_list is a list of lists with the courses.

new_list = [["Course1",  "Course2"], ["CourseA", "CourseB",  "CourseC"]]

Then you say, in effect:

if ["Course1",  "Course2"] == max([["Course1",  "Course2", "Course3"], ["CourseA", "CourseB"]]):
    return course

The problem is, that when you use max() on a list of a list of strings, the function will always return the list with the maximum alphabetical value for the first item of each list. Maximum alphabetical value means that "z" is greater than "a", because it's later in the alphabet. So, max(['a','b','c'], ['y', 'z']) will return ['y','z'] even though ['y','z'] is the shorter list, because 'y' has a higher alphabetical value than 'a'.

The point is, that your current approach can't work because you're comparing the alphabetical value of the course lists, instead of their lengths.

So we need a way to get the lengths of the course lists. Let's modify your function to do that:

def most_courses(sing_arg):
    new_list = []
    for course in sing_arg.keys():
        new_list.append(len(sing_arg[course]))
        if len(sing_arg[course]) == max(new_list):
            return course

Notice how I added len() before each sing_arg[course]. So now, new_list will contain the length of each course list, and our if statement will compare the length of our current course list against the maximum value in our new_list, which should be the length of the longest list of courses. But wait! Now we have another problem.

Since we are using the if statement inside of our for loop, in our first iteration for the loop, we will compare the length of the current list against the longest length in new_list, but at this point we've only been through our loop once, and we've only added one item to new_list, the length of our current course list!

So in effect, our conditional now says, if the length of the first course list equals the length of the first course list, return the first teacher's name. Since this will always be true, our function will now always return the name of the first teacher in our dictionary.

So, we need to wait until ALL the values are stored in new_list, THEN do our comparison, which means we'll need to loop through the dictionary twice. The first time through, we'll get the value of the longest course list, and the second time through, we'll compare the length of each course list against the maximum length, and return the name of the teacher if the values match.

Michael Hulet informed me that copypasta solutions are a no-no, so I will be good. Here's what we have so far, you can finish it no prob.

def most_courses(sing_arg):
    new_list = []
    for course in sing_arg.keys():
        new_list.append(len(sing_arg[course]))
    # loop through dictionary again, and return teacher with most courses

Just for fun, here's my solution:

def most_courses(courseList):
    max_courses = max(map(lambda x: len(x), courseList.values()))
    for key, value in courseList.items():
        #return teacher with most courses
Michael Hulet
Michael Hulet
47,912 Points

This is an extremely great answer! I have one thing to add, though: It just occurred to me that OP currently defines a most_courses function twice, which is at least confusing to the point that it should never be done. Make sure there's only 1 in the solution you submit!

Also, for future reference, it's generally not ok to post code that can be copied and pasted and pass a challenge without modification as part or all of an answer, but to be honest, I'd be really sad to see this answer modified, so I'm not gonna be the one to remove anything. Thanks for taking the time to make such an awesome answer!

Kyle Knapp
Kyle Knapp
21,526 Points

Thanks Michael Hulet, I didn't know that about the solution copy-pasting, and I agree that everyone should have a fair shot at solving themselves. So I made some adjustments.

Anthony Lopez
Anthony Lopez
963 Points

Thank you so much for taking the time to explain this Kyle! I learned a lot from your answer.

I totally thought max() counted and retrieved the largest value in the list. Thanks for explaining the alphabetical value portion.

I also gained a deeper understanding of loops with you explanation! That makes a lot of sense that if it's in the loop it will take the first course.

I do have a question about this though.

My reasoning was that if it was in the loop it would cycle through each of the courses and essentially test each one. Which I now know isn't good because it would be testing against an incomplete list.

But if it's outside of the loop, how do I know and how does it know which course to test from? Which key is actually stored inside of the course variable once the loop has completed?

Kyle Knapp
Kyle Knapp
21,526 Points

Hi Anthony,

Nothing is being stored in the course variable. course is simply a placeholder name for each value in sing_arg.keys().

In other words, when we use for course in sing_arg.keys(), we're not actually storing anything in the course variable. Instead, we're using course as a temporary storage value for each key in sing_arg.keys(), after each cycle through the loop, course changes to the next value in sing_arg.keys(). When each key is cycled through and the for loop exits, course will contain the value of the last key cycled through. Here's a code example:

>>> course_list = {'Andrew Chalkley': ['jQuery Basics', 'Node.js Basics'],
'Kenneth Love': ['Python Basics', 'Python Collections']}
>>> for course in course_list.keys():
    print(course)
# In the 1st cycle, course == "Andrew Chalkley"
Andrew Chalkley
# In the 2nd cycle, course == "Kenneth Love"
Kenneth Love

>>> print(course)
# After the for loop exits, course still hold the value of the last teacher's name
Kenneth Love

As you can see, the course variable doesn't really hold anything useful for solving our problem. It's just a temporary container used to process the dictionary data in our for loop.

Now, let me explain our approach to this problem. The problem asks us to look at every course list in the dictionary, and return the name of the teacher with the longest course list.

In our first for loop, we calculate the lengths of ['jQuery Basics', 'Node.js Basics'] and ['Python Basics', 'Python Collections'] and we store these lengths in new_list.

Since new_list contains the lengths of all course lists, we can now use max(new_list) to get the length of the longest course list. We can then compare that to our dictionary to see which teacher has the most courses.

In the second for loop, we cycle through our dictionary to compare the length of each course list to the longest length in new_list. If there's a match, we return the name of the teacher.

In code, that will look something like:

def most_courses(sing_arg):
    new_list = []
    for course in sing_arg.keys():
        new_list.append(len(sing_arg[course]))
    for course in sing_arg.keys():
        if len(sing_arg[course]) == max(new_list):
             # return value of teacher's name
Michael Hulet
Michael Hulet
47,912 Points

First of all, I love that you're using built-in functions instead of re-implementing them yourself inline. That being said, while I managed to work out a one-liner solution combining a few of these functions that should theoretically work, it's very unnecessarily complicated and the challenge doesn't seem to like it for every test case, so you probably don't wanna use the max function here. When given a dictionary, the max function only compares the keys with each other, which is rarely useful.

Instead, what I'd do here is loop over the dictionary and keep track of which key corresponds to the longest list and return that after the end of the loop

Anthony Lopez
Anthony Lopez
963 Points

Thanks Michael!

I had two functions with most_courses because I was asking why each of those wouldn't work as an example. I will definitely only include the correct one for my answer!