-1

I have a program to write in C++ that will generate files with the names of the same teachers, which are to be additionally sorted chronologically (by day and hour):

<hour> <day> <group> <surname> <subject>

(here is an example.txt content):

10:15-11:30 Friday gr1 Smith Programming
07:10-09:15 Wednesday gr2 Taylor InternetofThings
11:00-12:00 Monday gr2 Smith Java
10:20-11:45 Thursday gr1 Taylor Matchematic

and after working, program generate files:

Smith.txt :

11:00-12:00 Monday gr2 Java
10:15-11:30 Friday gr1 Programming

Taylor.txt :

07:10-09:15 Wednesday gr2 InternetofThings
10:20-11:45 Thursday gr1 Matchematic

What I've already managed to do is to load data from the txt file into the dynamic array (code below). I have no idea how to do a name search and sorting (names can be different and the number of lines can also be different). I was thinking about a loop that would look for the same letters from the "surname" variable of the dynamic array, but I don't know how to implement it.

struct Line {
  string hour;
  string day;
  string group;
  string surname;
  string subject; 
};

void readLine(ifstream& file, Line& line) {
  file >> line.hour >> line.day >> line.group >> line.surname >> line.subject;
}

void readLineTab(ifstream& file, Line* lineTab, const int numOfLines) {
  for (int i = 0; i < numOfLines; i++) {
    readLine(file, lineTab[i]); 
  }
}

void printLine(const Line& line) {
  cout << line.hour << " " << line.day << " " << line.group << " " << line.surname << " " << 
  line.subject << endl;
}

void printLineTab(Line* lineTab, const int size) {
  for (int i = 0; i < size; i++) {
    printLine(lineTab[i]);
  }
}

int checkFile(string& filePath, int& numOfLines) {
  ifstream file;
  file.open(filePath.c_str());
  if (file.fail()) {
    cerr << "Error file open: " << filePath << endl;
    file.close();
    return 1;
  }
  string line;
  int lineNr = 0;
  while (getline(file, line)) {
    lineNr++;
    numOfLines++;
  }
  file.close();
  return 0;
}

int main(int argc, char** argv) {
  int numOfLines = 0; 
  ifstream file; 
  string filePath = "example.txt"; 

  if (checkFile(filePath, numOfLines)) {
    return 1;
  }

  Line* lineTab = new Line[numOfLines];

  file.open(filePath.c_str());
  if (file.fail()) {
    cerr << "Error file open: " << filePath << endl;
    return 1;
  }

  readLineTab(file, lineTab, numOfLines);
  printLineTab(lineTab, numOfLines);

  delete[] lineTab;
  file.close();
  return 0;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    You probably want a `std::map` or a `std::unordered_map` to do this, but note, that the key string for these needs to be unique. If it isn't you'll need a `std::multimap`. You can find the documentation of those classes here: https://en.cppreference.com/w/cpp/container – πάντα ῥεῖ Dec 28 '20 at 18:04
  • Are you restricted from using container classes from the standard library? – drescherjm Dec 28 '20 at 18:04
  • A simple way would be to just read a line, check if a file for the teacher exists and if not create one. If it does exist, append the data to the file. – Rohan Harish Dec 28 '20 at 18:06
  • std::string.find(str2) is the tool used to find str2 within str. – ytlu Dec 28 '20 at 18:39
  • As you want to sort by day, you should convert the day name to a sortable week index (Monday=1, ..., Saturday=6). Then sort the array by 1/ surname, 2/ week index 3/ hour. When this is done, just write the sorted array, opening a new file if surname has changed (start with an empty surname to create a new file on first record). BTW, it will be easier to implement if you can use a `std::map` container. – Serge Ballesta Dec 28 '20 at 18:43

1 Answers1

0

A possible way is to sort the array by surname, then day, then hour. But to sort by days, you will have to convert the strings to a sortable index, for example from 1 for Monday to 6 to Saturday.

I would do the following additions to your code:

Add a week_index member the the Line struct, and build a map for the convertions:

struct Line {
    string hour;
    string day;
    int week_index;
    string group;
    string surname;
    string subject;
};

std::map<std::string, int> weekdays = {
    {"Monday", 1},
    {"Tuesday", 2},
    {"Wednesday", 3},
    {"Thursday", 4},
    {"Friday", 5},
    {"Saturday", 6}
};

void readLine(ifstream& file, Line& line) {
    file >> line.hour >> line.day >> line.group >> line.surname >> line.subject;
    line.week_index = weekdays[line.day];
}

Add a comparison function:

bool comp(const Line& first, const Line& second) {
    if (first.surname < second.surname) {
        return true;
    }
    if (first.surname > second.surname) {
        return false;
    }
    if (first.week_index < second.week_index) {
        return true;
    }
    if (first.week_index > second.week_index) {
        return false;
    }
    if (first.hour < second.hour) {
        return true;
    }
    return false;
}

Finaly, sort the array and write it to the relevant files:

...
readLineTab(file, lineTab, numOfLines);
printLineTab(lineTab, numOfLines);

std::sort(lineTab, lineTab + numOfLines, comp);

string surname = "";
std::ofstream ofs;
for (const Line* line = lineTab; line < lineTab + numOfLines; line++) {
    if (line->surname != surname) {
        if (ofs.is_open()) ofs.close();
        ofs.open(line->surname + std::string(".txt"));
        surname = line->surname;
    }
    ofs << line->hour << " " << line->day << " " << line->group << " " << line->subject << '\n';
}
ofs.close();

delete[] lineTab;
...
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • And I have one more question, can you do it without using std::map<> on vectors? – Vertcore Design Dec 30 '20 at 22:34
  • @VertcoreDesign: I have used a map here because I did not want to implement a linear search in an array, but it could be replaced by a plain array of structs (`struct { const char * day_name; int week_day;};`). – Serge Ballesta Dec 31 '20 at 08:31
  • You could represent if you would do it with this array instead of using map<> because I try but something doesn't work. – Vertcore Design Jan 09 '21 at 19:21