Q:

Finding out lowest scoring students in each subjects, using C++/STL

Each student has got marks (out of 10) in different subjects. Below is sample csv file having student_name, subject, marks.
How can we write an efficient program using appropriate data structures to parse this file, process it and generate output? Our output should print who has got the lowest marks (and subject).
john, maths, 8
roy, science, 6
john, science, 5
sara, arts, 7
neil, maths, 4
tony, arts, 6
bob, science, 7
tony, maths, 7
neil, science, 8
john, arts, 4
sara, history, 3

Our output should print who has got lowest marks for any subject (as compared with all others in that subject). So our output should be:
neil
john

since neil got lowest marks in maths than others, john got lowest marks in science and arts than others. (However, sara will not appear in output because sara has not got marks in history lower than anyone else in that subject)
What have I done?
I tried using two maps in this way: <subject, <name, marks>>
i.e.
std::unordered_map<std::string, std::unordered_map<std::string, int>> name_subject_marks; 
Here, I would add <name, marks> against each subject. And if the subject already exists I would replace associated <name, marks> only when existing marks are higher. Thus, finally I will get names of lowest marks for that subject.
However, sara is also appearing in this list. How can I handle it? Also please let me know if there is any better way to do this.

A:

Use a boolean flag to mark the first person of each subject. Iterate over each line. Split the line. Store the marks and names with the flag in a map. Iterate over the map to insert all names in a set. Iterate over the set to print the names.
#include <iostream>
#include <unordered_map>
#include <set>
#include <sstream>

int main() {
    std::string filename;
    std::cin >> filename;
    std::ifstream file(filename);
    std::unordered_map<std::string, std::tuple<unsigned short, std::string, bool>> lowestMarks;
    std::string line;
    while (std::getline(file, line)) {
        std::stringstream sline(line);
        std::string name;
        std::string subject;
        unsigned short mark;
        std::getline(sline, name, ',');
        std::getline(sline, subject, ' ');
        std::getline(sline, subject, ',');
        sline >> mark;
        auto lowestMark = lowestMarks.find(subject);
        if (lowestMark == lowestMarks.end()) {
            lowestMarks[subject] = std::make_tuple(mark, name, true);
        } else if (std::get<0>(lowestMark->second) > mark) {
            lowestMarks[subject] = std::make_tuple(mark, name, false);
        } else {
            std::get<2>(lowestMarks[subject]) = false;
        }
    }
    std::set<std::string> names;
    for (auto lm : lowestMarks) {
        if (std::get<2>(lm.second)) continue;
        names.insert(std::get<1>(lm.second));
    }
    for (auto name : names) {
        std::cout << name << '\n';
    }
    return 0;
}

The code is tested with clang++ -std=c++11. The output is
john
neil

