Skip to main content

Section 15.11 Case Study: Students

We have a datafile Students.txt below that has information about the students in a course. For each student there are 3 lines:
We want to read in this data and then calculate each student’s average. We may also want to calculate the class average and the highest average.
Data: Students.txt
Maxine Q. Craft
Active
80,93,64,68,87,70,84,83,97,71
Stella N. McFadden
Active
83,79,62,98,97,93,78,67,73,62
Oren C. Singleton
Inactive
86,81,69,77,78,76,84,97,68,69
Palmer R. Price
Active
84,98,98,87,61,96,89,77,73,73
Gray J. Witt
Active
61,68,70,75,96,84,61,71,85,87
Hilary B. Gill
Inactive
82,86,72,92,82,94,82,97,79,82
Ingrid A. Horne
Active
72,66,65,91,65,93,99,93,92,63
Byron U. Stout
Active
64,70,94,65,90,63,88,65,76,65

Subsection 15.11.1 Understanding the problem

We don’t necessarily need to store all of the data if all we want to do is print out each student’s average. We could try to read in a name, remember it, read in the scores after it, calculate the average, and then forget all of that data as we move on to the next student. But if we want to do multiple computations on the data it will be handy to store it. It will also make the code easier to write and test if we can think of getting and storing the data as separate from calculating with the data.
Just worrying about storing the data for now, we can break this problem into a few key parts:
  • Design our storage
  • Figure out how to create a student from two lines of text
  • Read in all of the students

Subsection 15.11.2 Storing a student

For each Student we need to store the name and the grades. While we could store the name as a single string, it might be helpful to have the first, middle and last names separated so that we can easily print a name in different formats (e.g. β€œfirst, last” or β€œfirst last”).
Then we have the status. We know there are only two possible values to store, so it would make sense to make an enumeration for them. We could of course just store the string "Active" or "Inactive", but an enumeration is more efficient to store and work with. And we won’t have to worry about typos as much. The compiler can’t identify status == "Atcive" as an error, but it can identify that status == Status.ATCIVE doesn’t make sense.
Finally we need a list of scores, which sounds like a vector.
In main, we will store the entire roster as a vector of type Student:
#include <iostream>
#include <vector>
#include <string>
using namespace std;

struct Name {
    string first;
    string middle;
    string last;
};

enum class Status {
    INACTIVE,
    ACTIVE
};

struct Student {
    Name name;
    Status currentStatus;
    vector<double> scores;
};

int main() {
    vector<Student> studentList;
}

Subsection 15.11.3 Parsing a Name

Now that we know how we want to store the data, we can worry about creating a student from three lines of text. There will be a significant amount of logic in turning text like Byron U. Stout or 64,70,94,65,90,63,88,65,76,65 into the right data. So to make the job easier to tackle, let’s break it up.
First we will just worry about turning a string like Byron U. Stout into a Name struct. We could either find each space and use those to chop up the string. But since the tokens are separated by whitespaces, we could also use a stringstream to "read" the data from the string into the parts of a name. Complete the code to do this job using a stringstream:

Checkpoint 15.11.1.

Construct the algorithm to produce a name struct from a string.

Subsection 15.11.4 Getting the Status

Next we need a function to turn the string Active or Inactive into a Status value.
We can use a simple if statement to check the string and return the right value. Write the code for this function:

Checkpoint 15.11.2.

Construct the algorithm to return either Status::ACTIVE or Status::INACTIVE based on the contents of the parameter line. Throw a logic_error if line is not "Active" or "Inactive":

Subsection 15.11.5 Parsing the Scores

Finally we need to parse the scores. We need to take the comma separated values and break them into individual values and store each into a vector. We can’t use a stringstream for this since the tokens we want (the numbers) are separated by commas and not whitespace. So instead, we will have to use logic like this:
Listing 15.11.1.
while there is a comma in the string
    get the text up to the comma
    turn it into a number
    add it to the vector
    remove the text up to the comma from the string
turn the remaining text into a number
add it to the vector

Checkpoint 15.11.3.

Construct the algorithm to produce a vector of doubles from the line of scores. Note that the function takes line by value so we can safely modify the copy that results. If we took it by const reference, we would not be able to modify line by removing one part at a time.

Subsection 15.11.6 Building the Student

With those helper functions in place, it is easy to build a student. Given the three lines of data, we can call the three functions we have already written and assign their results into the right members of a Student struct:
Listing 15.11.2.
Student createStudent(const string& nameLine,
                      const string& statusLine,
                      const string& scoresLine) {
    Student student;
    student.name = createName(nameLine);
    student.currentStatus = createStatus(statusLine);
    student.scores = createScores(scoresLine);
    return student;
}
You have attempted of activities on this page.