Section 5. Nested Dictionaries & Classes


Section materials curated by our head TA Iddah Mlauzi, drawing upon materials from previous quarters.

Here's the Section 5 starter code. You'll unzip/extract this file and open it in PyCharm just like you do with your assignment starter code.

In this section, you'll learn how to work with JSON structures and handle nested dictionaries. The dataset used in this problem contains three layers of nesting, similar to the Infinite Story assignment, which will help you practice navigating through complex data structures.

Network

In this problem, you'll analyze a social network dataset stored in a JSON file.

Your Data

You have a dataset in JSON format that contains information about people and their friends. Each person has a list of their friends, and for each friend, we store how many years they've been friends. Each person's data in the social network is structured something like this:

{
    "network": {
        "Kavel": {
            "friends": [
                {
                    "name": "Clinton",
                    "years_friends": 6
                },
                {
                    "name": "Diego",
                    "years_friends": 2
                }
            ]
        },
        "Clinton": {
            "friends": [
                {
                    "name": "Kavel",
                    "years_friends": 6
                },
                {
                    "name": "Anita",
                    "years_friends": 4
                }
            ]
        }
    }
}

Your actual dataset is much larger – go check it out in the file named data.json!

Find Missing Friends

Given filename which contains a JSON representation of a social network, your first task is to find and print the names of the friends who are mentioned but are missing from the network. Your code should avoid printing out names twice.

def find_missing_friends(filename):
    pass  # your code here

For example, using the small JSON dataset above:

  • Kavel is friends with Clinton and Diego. While Clinton’s data exists in the JSON, Diego's does not.
  • Clinton is friends with Anita and Kavel. While Kavel’s data exists, Anita’s does not.

The program should print the names of these "missing" friends, which in this case would be:

Diego
Anita

Run the provided doctest to make sure your code is working.

Bonus: Calculate Total Friendship Years

Write a function that, given the filename of the JSON social‑network data and a person’s name, prints the total number of years that person has been friends with everyone in their list.

def calculate_total_years(filename, name):
    pass  # your code here

Using the small example network above:

Clinton has a total of 10 years of friendship.

If the name is not in the network (for example "Langston"), your program should print

Langston is not in the network.
def find_missing_friends(filename):
    """
    Given a JSON file, find and print the names of
    the friends who are mentioned but are missing from the network.

    >>> find_missing_friends("data.json")
    Michael
    Koa
    Haipeng
    Eli
    Kavita
    William
    Jose
    Nika
    """

    with open(filename, "r") as file:
       input_dict = json.load(file)

    data = input_dict["network"]
    missing_friends = []
    for person in data:
        person_data = data[person]
        persons_friends = person_data["friends"]
        for friend in persons_friends:
            name = friend["name"]
            if name not in data and name not in missing_friends:
                print(name)
                missing_friends.append(name)


def calculate_total_years(filename, person):
    """
    Given a JSON file and person,
    calculate and print the total number of years a given person has been friends with their friends.


    >>> calculate_total_years("data.json", "Sabino")
    Sabino has a total of 10 years of friendship.


    >>> calculate_total_years("data.json", "Langston")
    Langston is not in the network.
    """


    with open(filename, "r") as file:
        input_dict = json.load(file)
    data = input_dict["network"]


    # Check if the person is in the network
    if person not in data:
        print(f"{person} is not in the network.")
        return


    # Calculate the total years of friendship
    total_years = 0
    person_dict = data[person]
    for friend in person_dict["friends"]:
        total_years += friend["years_friends"]


    print(f"{person} has a total of {total_years} years of friendship.")


Circle Class

In this problem you are going to write the Circle class in the circle.py file for use in the following program. The design of the circle class is up to you, but running the program below should give the output below.

# import the Circle class from circle.py
from circle import Circle 

def main():
    # construct circle with radius 5
    circle = Circle(5) 

    # print the area of the circle
    print("The area of the circle is " + str(circle.get_area()))

    # print the circumference of the circle
    print("The circumference of the circle is " + str(circle.get_circumference()))

if __name__ == "__main__":
    main()

After running the above program, the terminal output should be:

The area of the circle is 78.53981633974483
The circumference of the circle is 31.41592653589793

You can test your circle code with radius 5 by entering python3 circle_test.py (using py or python on Windows).

As a reminder, a circle with radius r has area πr^2 and circumference 2πr. The value of π is stored in the constant math.pi, which you can access with the math module which we've imported at the top of the starter file.

Design question: What information should we store in the circle class? What methods need to exist?

"""
File: circle.py
----------------
This file defines a Class for a Circle with some radius, with
methods for getting the area and circumference of the circle.
"""

import math


class Circle:

    def __init__(self, radius):
        """
        Creates a Circle object and initializes the radius
        """
        self.radius = radius  # we'll want to keep track of radius

    def get_area(self):
        """
        Returns the area of this circle
        """
        area = math.pi * (self.radius ** 2)
        return area

    def get_circumference(self):
        """
        Returns the circumference of this circle
        """
        circumference = math.pi * self.radius * 2
        return circumference

Roster Class

We're going to implement a class that stores information about students enrolled in a class at Stanford. Specifically, your Roster class will have the following methods:

  • __init__(self) – initializes a new Roster instance and any member variables
  • add_student(self, name, id) – adds a student by name (str) and id (int) to the class roster
  • get_id(self, name) – returns the id of the student with this name, or –1 if this student isn't in the class
  • get_roster(self) – returns a list of the names of all students enrolled in the class

Here's how your class might be used from another program:

def main():
    cs106a = Roster()
    cs106a.add_student('Muhammad', 12345678)
    print(cs106a.get_roster())          # prints ['Muhammad']
    cs106a.add_student('Elyse', 56781234)
    print(cs106a.get_roster())          # prints ['Muhammad', 'Elyse']
    print(cs106a.get_id('Maria'))       # prints -1
    print(cs106a.get_id('Muhammad'))    # prints 12345678


if __name__ == '__main__':
    main()

Here's your starter code:

# File: Roster.py

class Class:

    def __init__(self):
        """
        Creates a new instance of the Roster class
        """
        pass

    def add_student(self, name, id):
        """
        Adds a student by name (string) and id (int) to the class
        """
        pass

    def get_id(self, name):
        """
        Returns the id of the student with this name, or -1 if this
        student isn't in the class
        """
        pass

    def get_roster(self):
        """
        Returns a list of the names of all students enrolled in 
        the class
        """
        pass

You can test your roster code by entering python3 roster_test.py (using py or python on Windows).

# File: Roster.py

class Roster:

    def __init__(self):
        """
        Creates a new instance of the Roster class
        """
        self.students = {}   # instance variable to keep track of the student/id pairs

    def add_student(self, name, id):
        """
        Adds a student by name (string) and id (int) to the class
        """
        self.students[name] = id

    def get_id(self, name):
        """
        Returns the id of the student with this name, or -1 if this
        student isn't in the class
        """
        if name in self.students:
            return self.students[name]
        return -1

    def get_roster(self):
        """
        Returns a list of the names of all students enrolled in the class
        """
        student_lst = []
        for student in self.students:
            student_lst.append(student)
        return student_lst