Lesson 3
Managing Student Enrollments with Go Maps
Introduction

Welcome to today's lesson! We'll explore a practical application of managing student enrollments for various courses using Go maps. Imagine you're operating an online course platform, and you need to efficiently handle enrollments, checks, and listings of students in different courses. Go maps are ideally suited for this problem due to their efficient lookups and storage operations. Furthermore, maps can emulate set behavior, helping us ensure that a student cannot enroll in a course more than once.

By the end of this session, you'll gain practical expertise in managing dynamic collection-like data with maps. Let’s dive in!

Introducing Methods to Implement

Here are the methods we need to implement in our enrollment system:

  • func (es *EnrollmentSystem) enroll(student string, course string): This method adds a student to a course. If the student is already enrolled, it does nothing, effectively behaving like a set addition.
  • func (es *EnrollmentSystem) unenroll(student string, course string) bool: This method removes a student from a course. It returns true if the student was enrolled and is now removed. Otherwise, it returns false. If, after unenrolling the student, the course becomes empty, the course is removed from the system.
  • func (es *EnrollmentSystem) isEnrolled(student string, course string) bool: This method checks if a student is currently enrolled in a given course. It returns true if the student is enrolled, and false otherwise.
  • func (es *EnrollmentSystem) listStudents(course string) []string: This method returns a list of all students enrolled in a specific course. It returns an empty slice if no students are enrolled.

Let's look at how to implement each of these methods step-by-step.

Step 1: Define the Struct

To start, we'll define our struct and gradually add each method.

First, we define our EnrollmentSystem struct:

Go
1package main 2 3type EnrollmentSystem struct { 4 enrollments map[string]map[string]struct{} 5} 6 7func NewEnrollmentSystem() *EnrollmentSystem { 8 return &EnrollmentSystem{enrollments: make(map[string]map[string]struct{})} 9}

In this code, we initialize an EnrollmentSystem struct with an enrollments map, mapping courses to sets of students. Let's take a deeper look at the type map[string]map[string]struct{}, employed by enrollments:

  • Outer map: Maps course strings to a corresponding inner map. The key is the course name.
  • Inner map: Emulates a student set for each course. The keys are the student names, and the values are of type struct{}, which is a zero-size type in Go—perfect for set-like behavior without any additional overhead.
Step 2: Implement enroll Method

Next, we tackle the enroll method:

Go
1// ... previous code 2 3func (es *EnrollmentSystem) enroll(student string, course string) { 4 if es.enrollments[course] == nil { 5 es.enrollments[course] = make(map[string]struct{}) 6 } 7 es.enrollments[course][student] = struct{}{} 8} 9 10// Example usage: 11func main() { 12 es := NewEnrollmentSystem() 13 es.enroll("Alice", "Math101") 14}

In the enroll method, the course is checked if it exists. If not, a new map is created for the course, effectively initializing the set. The student's enrollment is then added using this map. This setup ensures each student is only stored once per course, enforcing set-like uniqueness (i.e. no duplicate entries).

Step 3: Implement unenroll Method

Now, let's implement the unenroll method:

Go
1// ... previous code 2 3func (es *EnrollmentSystem) unenroll(student string, course string) bool { 4 students, exists := es.enrollments[course] 5 if !exists { 6 return false 7 } 8 9 if _, enrolled := students[student]; enrolled { 10 delete(students, student) 11 if len(students) == 0 { 12 delete(es.enrollments, course) 13 } 14 return true 15 } 16 return false 17} 18 19// Example usage: 20func main() { 21 es := NewEnrollmentSystem() 22 es.enroll("Alice", "Math101") 23 es.enroll("Bob", "Math101") 24 es.unenroll("Alice", "Math101") 25 es.unenroll("Bob", "Math101") 26}

This method checks if both the course and the student exist. If they do, the student is removed. Importantly, we also check if the course map is empty after unenrolling, and if so, the entire course is pruned from the system, retaining a clean state for new enrollments. The method returns a boolean indicating successful unenrollment.

Step 4: Implement isEnrolled Method

Next, let's focus on the isEnrolled method:

Go
1// ... previous code 2 3func (es *EnrollmentSystem) isEnrolled(student string, course string) bool { 4 students, exists := es.enrollments[course] 5 if !exists { 6 return false 7 } 8 _, enrolled := students[student] 9 return enrolled 10} 11 12// Example usage: 13func main() { 14 es := NewEnrollmentSystem() 15 es.enroll("Alice", "Math101") 16 println(es.isEnrolled("Alice", "Math101")) // Output: true 17 println(es.isEnrolled("Bob", "Math101")) // Output: false 18}

In this simple method, both the existence of the course and the student's enrollment are checked. The method returns true or false rapidly by exploiting the map's efficient lookup capabilities.

Step 5: Implement listStudents Method

Finally, we implement the listStudents method:

Go
1// ... previous code 2 3func (es *EnrollmentSystem) listStudents(course string) []string { 4 students, exists := es.enrollments[course] 5 if !exists { 6 return []string{} 7 } 8 9 studentList := []string{} 10 for student := range students { 11 studentList = append(studentList, student) 12 } 13 return studentList 14} 15 16// Example usage: 17func main() { 18 es := NewEnrollmentSystem() 19 es.enroll("Alice", "Math101") 20 es.enroll("Bob", "Math101") 21 fmt.Println(es.listStudents("Math101")) // Output: [Alice Bob] (order is not guaranteed) 22}

This method collects student names into a slice and returns it. The iteration over the map ensures we capture all enrolled students, reflecting the dynamic nature of maps where the order is not guaranteed.

Lesson Summary

In today's lesson, we enriched our understanding of how to leverage Go maps to manage student enrollments effectively, using them to simulate set behavior. We implemented methods to enroll and unenroll students, check enrollments, and list students in a course. Each of these methods demonstrated practical use of maps in organizing and maintaining dynamic data structures, while reinforcing your understanding of using maps and slices in Go.

I encourage you to move on to the practice to undertake similar challenges to deepen your understanding. Keep experimenting and honing your skills. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.