Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

6.11 LAB: Course gradebook with unordered_map Step 1: Inspect the Gradebook abstract base class The read-only Gradebook.h file has a declaration for the Gradebook abstract

6.11 LAB: Course gradebook with unordered_map

Step 1: Inspect the Gradebook abstract base class

The read-only Gradebook.h file has a declaration for the Gradebook abstract base class. Access Gradebook.h by clicking on the orange arrow next to main.cpp at the top of the coding window.

The Gradebook class stores a collection of entries for a course. Conceptually, a gradebook entry is a (assignment name, student ID, score) triplet. Each assignment name is a string, each student ID an integer, and each score is a double. A score is entered into the gradebook via the SetScore() function.

The Gradebook class has six pure virtual functions that must be implemented in an inheriting class.

Step 2: Add member variables to CourseGradebook class

The CourseGradebook class inherits from Gradebook and is declared in CourseGradebook.h. Inspect each function that must be implemented. Choose one or more member variables to store gradebook data and add the member variables to the CourseGradebook class. Several options exist, so grading does not analyze the choice of member variables.

Step 3: Implement CourseGradebook's SetScore() and GetScore() functions

Implement the SetScore() function to enter a single entry into the gradebook. SetScore() has parameters for the assignment name, student ID, and score. How the entry is stored depends on the member variables chosen in step 2.

Implement the GetScore() function to get a single score from the gradebook. GetScore() has parameters for the assignment name and student ID, and returns a double for the corresponding score. NAN is returned if no such entry exists in the gradebook.

Code in main.cpp calls TestGetScoreAndSetScore() to test both functions. Run your program in develop mode and ensure that the test passes before proceeding further.

Step 4: Implement the remaining CourseGradebook functions

Implement the remaining functions according to the specifications below.

  • GetAssignmentScores() takes a string for the assignment name and returns an unordered_map that maps a student ID to the student's corresponding score for the assignment. An entry exists in the returned map only if a score has been entered with the SetScore() function. An empty map is returned if no such assignment exists in the grade book.
  • GetSortedAssignmentNames() returns a vector with all distinct assignment names, sorted in ascending order.
  • GetSortedStudentIDs() returns a vector with all distinct student IDs, sorted in ascending order.
  • GetStudentScores() gets all scores that exist in the gradebook for the student whose ID equals the function parameter. Scores are returned as an unordered_map that maps an assignment name to the student's corresponding score for that assignment.
  • If needed, implement CourseGradebook's destructor to free any dynamically allocated content stored in the gradebook.

main.cpp ( only read)

#include #include #include #include #include "Gradebook.h" #include "CourseGradebook.h" #include "TestUtility.h" using namespace std;

bool TestGetScoreAndSetScore(); bool TestGetAssignmentScores(); bool TestGetSortedAssignmentNames(); bool TestGetSortedStudentIDs(); bool TestGetStudentScores();

int main(int argc, char *argv[]) { bool result1 = TestGetScoreAndSetScore(); bool result2 = TestGetAssignmentScores(); bool result3 = TestGetSortedAssignmentNames(); bool result4 = TestGetSortedStudentIDs(); bool result5 = TestGetStudentScores(); cout << endl << "Summary:" << endl; cout << "TestGetScoreAndSetScore(): " << (result1 ? "PASS" : "FAIL") << endl; cout << "TestGetAssignmentScores(): " << (result2 ? "PASS" : "FAIL") << endl; cout << "TestGetSortedAssignmentNames(): " << (result3 ? "PASS" : "FAIL") << endl; cout << "TestGetSortedStudentIDs(): " << (result4 ? "PASS" : "FAIL") << endl; cout << "TestGetStudentScores(): " << (result5 ? "PASS" : "FAIL") << endl; cout << endl;

return 0; }

bool TestGetScoreAndSetScore() { cout << endl << "---- TestGetScoreAndSetScore() ----" << endl; // Create a gradebook with sample data for testing CourseGradebook gradebook = TestUtility::MakeSampleGradebook(); // Each test case is a (assignmentName, studentID, expectedScore) tuple std::tuple testCases[] = { std::make_tuple("Midterm", 11111, 91.0), std::make_tuple("Homework 1", 22222, NAN), // no entry std::make_tuple("Homework 3", 55555, 71.5), std::make_tuple("Course project", 66666, 0.0), std::make_tuple("Homework 2", 10000, 90.0), std::make_tuple("Homework 4", 55555, 77.5), std::make_tuple("Homework 5", 33333, NAN), // no such assignment std::make_tuple("Final exam", 44444, NAN), // no entry std::make_tuple("Homework 2", 77777, 76.0), std::make_tuple("Homework 1", 88888, 64.5) }; // Iterate through test cases for (auto testCase : testCases) { string assignmentName = std::get<0>(testCase); int studentID = std::get<1>(testCase); double expected = std::get<2>(testCase); double actual = gradebook.GetScore(assignmentName, studentID); // Reminder: Can't compare NAN with ==, so a special case is needed bool areEqual = isnan(expected) ? isnan(actual) : (actual == expected); if (areEqual) { cout << "PASS: GetScore(\"" << assignmentName << "\", "; cout << studentID << ") returned " << actual << endl; } else { cout << "FAIL: GetScore(\"" << assignmentName << "\", "; cout << studentID << ") returned " << actual; cout << ", but expected is " << expected << endl; return false; } } return true; }

bool TestGetAssignmentScores() { cout << endl << "---- TestGetAssignmentScores() ----" << endl; // Create a gradebook with sample data for testing CourseGradebook gradebook = TestUtility::MakeSampleGradebook(); unordered_map hw2Scores = { pair(11111, 89.0), pair(22222, 75.0), pair(33333, 100.0), pair(44444, 50.0), pair(55555, 76.5), pair(66666, 84.5), pair(77777, 76.0), pair(88888, 74.5), pair(99999, 100.0), pair(10000, 90.0), pair(90000, 85.0) }; unordered_map midtermScores = { pair(11111, 91.0), pair(22222, 77.5), pair(33333, 88.0), pair(44444, 40.0), pair(55555, 64.5), pair(66666, 91.0), pair(77777, 75.0), pair(88888, 88.0), pair(99999, 88.0), pair(10000, 92.0), pair(90000, 90.0) }; unordered_map projectScores = { pair(11111, 100.0), pair(22222, 60.0), pair(33333, 90.0), //pair(44444, 80.0), // no entry for student 44444 pair(55555, 87.0), pair(66666, 0.0), pair(77777, 72.0), pair(88888, 85.5), pair(99999, 80.0), pair(10000, 77.5), pair(90000, 85.0) }; // Each test case is a (assignmentName, mapOfExpectedScores) pair vector*>> testCases = { make_pair("Homework 2", &hw2Scores), make_pair("Midterm", &midtermScores), make_pair("Course project", &projectScores) }; // Iterate through all test cases for (auto& testCase : testCases) { string assignmentName = testCase.first; unordered_map& expectedMap = *(testCase.second); // Get the actual map from the gradebook cout << "Calling GetAssignmentScores(\"" << assignmentName; cout << "\")" << endl; unordered_map actualMap = gradebook.GetAssignmentScores(assignmentName); // Compare sizes first if (expectedMap.size() != actualMap.size()) { cout << "FAIL: GetAssignmentScores(\"" << assignmentName; cout << "\") returned a map with "; if (1 == actualMap.size()) { cout << "1 score, "; } else { cout << actualMap.size() << " scores, "; } cout << "but the expected map has " << expectedMap.size(); cout << " scores" << endl; return false; } // Sizes are equal, so now compare each ID/score pair for (auto expectedPair : expectedMap) { const int studentID = expectedPair.first; if (0 == actualMap.count(studentID)) { cout << "FAIL: GetAssignmentScores(\"" << assignmentName; cout << "\") returned a map that is missing an entry "; cout << "for student ID " << studentID << endl; return false; } // Actual map has student ID, so now compare corresponding score double expectedScore = expectedPair.second; double actualScore = actualMap[studentID]; bool areEqual = isnan(expectedScore) ? isnan(actualScore) : (actualScore == expectedScore); if (!areEqual) { cout << "FAIL: GetAssignmentScores(\"" << assignmentName; cout << "\") returned a map that has a score of "; cout << actualScore << " for student ID " << studentID; cout << ", but the expected score is "; cout << expectedScore << endl; return false; } } // All entries match cout << "PASS: GetAssignmentScores(\"" << assignmentName; cout << "\") returned a map with " << actualMap.size(); cout << " correct scores" << endl; } return true; }

bool TestGetSortedAssignmentNames() { cout << endl << "---- TestGetSortedAssignmentNames() ----" << endl; CourseGradebook gradebook = TestUtility::MakeSampleGradebook(); vector expected = { "Course project", "Final exam", "Homework 1", "Homework 2", "Homework 3", "Homework 4", "Midterm" }; vector actual = gradebook.GetSortedAssignmentNames(); bool areEqual = true; if (actual.size() == expected.size()) { // Compare elements in order for (size_t i = 0; areEqual && i < expected.size(); i++) { if (actual[i] != expected[i]) { areEqual = false; } } } else { areEqual = false; } // Show pass or fail message along with expected and actual vector contents if (areEqual) { cout << "PASS: GetSortedAssignmentNames()" << endl; } else { cout << "FAIL: GetSortedAssignmentNames()" << endl; } cout << " Expected: { "; TestUtility::PrintVector(expected, std::cout, ", "); cout << " }" << endl << " Actual: { "; TestUtility::PrintVector(actual, std::cout, ", "); cout << " }" << endl; return areEqual; }

bool TestGetSortedStudentIDs() { cout << endl << "---- TestGetSortedStudentIDs() ----" << endl; CourseGradebook gradebook = TestUtility::MakeSampleGradebook(); vector expected = { 10000, 11111, 22222, 33333, 44444, 55555, 66666, 77777, 88888, 90000, 99999 }; vector actual = gradebook.GetSortedStudentIDs(); bool areEqual = true; if (actual.size() == expected.size()) { // Compare elements in order for (size_t i = 0; areEqual && i < expected.size(); i++) { if (actual[i] != expected[i]) { areEqual = false; } } } else { areEqual = false; } // Show pass or fail message along with expected and actual vector contents if (areEqual) { cout << "PASS: GetSortedStudentIDs()" << endl; } else { cout << "FAIL: GetSortedStudentIDs()" << endl; } cout << " Expected: { "; TestUtility::PrintVector(expected, std::cout, ", "); cout << " }" << endl << " Actual: { "; TestUtility::PrintVector(actual, std::cout, ", "); cout << " }" << endl; return areEqual; }

bool TestGetStudentScores() { cout << endl << "---- TestGetStudentScores() ----" << endl; CourseGradebook gradebook = TestUtility::MakeSampleGradebook(); unordered_map student22222Scores = { // Student has no score for "Homework 1" pair("Homework 2", 75.0), pair("Midterm", 77.5), pair("Homework 3", 80.5), pair("Homework 4", 81.0), pair("Course project", 60.0), pair("Final exam", 54.0) }; unordered_map student44444Scores = { pair("Homework 1", 60.0), pair("Homework 2", 50.0), pair("Midterm", 40.0), pair("Homework 3", 30.0) // Student has no score for "Homework 4" // Student has no score for "Course project" // Student has no score for "Final exam" }; unordered_map student88888Scores = { pair("Homework 1", 64.5), pair("Homework 2", 74.5), pair("Midterm", 88.0), pair("Homework 3", 84.0), pair("Homework 4", 84.0), pair("Course project", 85.5), pair("Final exam", 81.5) }; unordered_map student90000Scores = { pair("Homework 1", 80.0), pair("Homework 2", 85.0), pair("Midterm", 90.0), pair("Homework 3", 95.0), pair("Homework 4", 100.0), pair("Course project", 85.0), pair("Final exam", 94.5) }; // Each test case is a (studentID, mapOfExpectedScores) pair vector*>> testCases = { make_pair(22222, &student22222Scores), make_pair(44444, &student44444Scores), make_pair(88888, &student88888Scores), make_pair(90000, &student90000Scores) }; // Iterate through all test cases for (auto& testCase : testCases) { int studentID = testCase.first; unordered_map& expectedMap = *(testCase.second); // Get the actual map from the gradebook cout << "Calling GetStudentScores(" << studentID; cout << ")" << endl; unordered_map actualMap = gradebook.GetStudentScores(studentID); // Compare sizes first if (expectedMap.size() != actualMap.size()) { cout << "FAIL: GetStudentScores(" << studentID; cout << ") returned a map with "; if (1 == actualMap.size()) { cout << "1 score, "; } else { cout << actualMap.size() << " scores, "; } cout << "but the expected map has " << expectedMap.size(); cout << " scores" << endl; return false; } // Sizes are equal, so now compare each assignment name/score pair for (auto expectedPair : expectedMap) { string assignmentName = expectedPair.first; if (0 == actualMap.count(assignmentName)) { cout << "FAIL: GetStudentScores(" << studentID; cout << ") returned a map that is missing an entry for "; cout << "assignment \"" << assignmentName << "\"" << endl; return false; } // Actual map has assignment name, so now compare corresponding score double expectedScore = expectedPair.second; double actualScore = actualMap[assignmentName]; bool areEqual = isnan(expectedScore) ? isnan(actualScore) : (actualScore == expectedScore); if (!areEqual) { cout << "FAIL: GetStudentScores(" << studentID; cout << ") returned a map that has a score of "; cout << actualScore << " for assignment \""; cout << assignmentName << "\", but the expected score is "; cout << expectedScore << endl; return false; } } // All entries match cout << "PASS: GetStudentScores(" << studentID; cout << ") returned a map with " << actualMap.size(); cout << " correct scores" << endl; } return true; }

Gradebook.h (only read)

#ifndef GRADEBOOK_H #define GRADEBOOK_H

#include #include #include #include

class Gradebook { public: virtual ~Gradebook() { }

// GetScore() returns the specified student's score for the specified // assignment. NAN is returned if either: // - the assignment does not exist in the gradebook, or // - the assignment exists but no score exists for the specified student. virtual double GetScore(std::string assignmentName, int studentID) = 0; // SetScore() adds or updates a score in the gradebook. virtual void SetScore(std::string assignmentName, int studentID, double score) = 0; // GetAssignmentScores() returns an unordered_map that maps a student ID to // the student's corresponding score for the specified assignment. An entry // exists in the returned map only if a score has been entered with the // SetScore() function. virtual std::unordered_map GetAssignmentScores( std::string assignmentName) = 0;

// GetSortedAssignmentNames() returns a vector with all distinct assignment // names, sorted in ascending order. virtual std::vector GetSortedAssignmentNames() = 0; // GetSortedStudentIDs() returns a vector with all distinct student IDs, // sorted in ascending order. virtual std::vector GetSortedStudentIDs() = 0; // GetStudentScores() gets all scores that exist in the gradebook for the // student whose ID matches the function parameter. GetStudentScores() // returns an unordered_map that maps an assignment name to the student's // corresponding score for that assignment. virtual std::unordered_map GetStudentScores( int studentID) = 0; };

#endif

CourseGradebook.h ( edit)

#ifndef COURSEGRADEBOOK_H #define COURSEGRADEBOOK_H

#include #include "Gradebook.h"

class CourseGradebook : public Gradebook { protected: // Your code here

public: virtual ~CourseGradebook() { // Your code here, if needed }

std::unordered_map GetAssignmentScores( std::string assignmentName) override { // Your code here (remove placeholder line below) return std::unordered_map(); }

double GetScore(std::string assignmentName, int studentID) override { // Your code here (remove placeholder line below) return NAN; } std::vector GetSortedAssignmentNames() override { // Your code here (remove placeholder line below) return std::vector(); } // GetSortedStudentIDs() returns a vector with all distinct student IDs, // sorted in ascending order. std::vector GetSortedStudentIDs() override { // Your code here (remove placeholder line below) return std::vector(); } // GetStudentScores() gets all scores that exist in the gradebook for the // student whose ID equals the studentID parameter. Scores are returned as // an unordered_map that maps an assignment name to the student's // corresponding score for that assignment. std::unordered_map GetStudentScores( int studentID) override { // Your code here (remove placeholder line below) return std::unordered_map(); }

void SetScore(std::string assignmentName, int studentID, double score) override { // Your code here } };

#endif

TestUtility.h(only read)

#ifndef TESTUTILITY_H #define TESTUTILITY_H

#include #include #include #include #include #include "Gradebook.h" #include "CourseGradebook.h"

class TestUtility { public: // Populates a CourseGradebook from a vector of rows. Each row is a vector // of strings. Row 0 must be the header row. Column 0 must be the student ID // column. static void PopulateGradebookFromRows(Gradebook& gradebook, const std::vector>& rows) { using namespace std; // Iterate through non-header rows for (size_t rowIndex = 1; rowIndex < rows.size(); rowIndex++) { auto row = rows[rowIndex]; // Parse out student ID first int studentID = stoi(row[0]); // Call SetScore for each non-empty entry for (size_t colIndex = 1; colIndex < row.size(); colIndex++) { string entry = row[colIndex]; if (entry.length() > 0) { // Get the assignment name from the header row string assignmentName = rows[0][colIndex]; // Convert score from string to double double score = stod(entry); // Add to gradebook gradebook.SetScore(assignmentName, studentID, score); } } } }

// Returns a sample gradebook to use for testing purposes. static CourseGradebook MakeSampleGradebook() { using namespace std; vector> rows; rows.push_back({ "Student ID", "Homework 1", "Homework 2", "Midterm", "Homework 3", "Homework 4", "Course project", "Final exam" }); rows.push_back({ "11111", "92", "89", "91", "100", "100", "100", "95" }); rows.push_back({ "22222", "", "75", "77.5", "80.5", "81", "60", "54" }); rows.push_back({ "33333", "100", "100", "88", "100", "100", "90", "77.5" }); rows.push_back({ "44444", "60", "50", "40", "30", "", "", "" }); rows.push_back({ "55555", "73.5", "76.5", "64.5", "71.5", "77.5", "87", "63.5" }); rows.push_back({ "66666", "82.5", "84.5", "91", "92.5", "86", "0", "97" }); rows.push_back({ "77777", "77", "76", "75", "74", "73", "72", "71" }); rows.push_back({ "88888", "64.5", "74.5", "88", "84", "84", "85.5", "81.5" }); rows.push_back({ "99999", "100", "100", "88", "100", "100", "80", "79" }); rows.push_back({ "10000", "88", "90", "92", "87", "88.5", "77.5", "90" }); rows.push_back({ "90000", "80", "85", "90", "95", "100", "85", "94.5" }); CourseGradebook gradebook; PopulateGradebookFromRows(gradebook, rows); return gradebook; } template static void PrintVector(const std::vector& vectorToPrint, std::ostream& output, std::string separator = ",") { using namespace std; // Special case for empty vector if (0 == vectorToPrint.size()) { return; } // Print first element with no separator output << vectorToPrint[0]; // Print remaining elements, each preceded by the separator for (size_t i = 1; i < vectorToPrint.size(); i++) { output << separator << vectorToPrint[i]; } } };

#endif

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Professional Android 4 Application Development

Authors: Reto Meier

3rd Edition

1118223853, 9781118223857

More Books

Students also viewed these Programming questions