0
nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3
kb93,Kyle,Borris,Aetna,2

I am trying to take a CSV input file like above, store it, sort it by insurance (col 4), and then write it to diff files based on insurance but in alphabetical order by last name.

So in this program, I have a vector of uniqueInsurances, which in turn have a vector of enrollees. It is this vector of enrollees that I want to sort alphabetically by last name (col 3), so that if uniqueInsurances[0].name is Aetna, then uniqueInsurances[0].enrollees[] will have Kyle Borris listed BEFORE Nate Matthews. Right now I have it stored the other way with Nate Matthews listed before Kyle Borris.

I think it's due to the vector of vectors and nested for loops required for this problem that's getting me mixed up, so I was wondering if someone could help guide me in terms of the best way to sort the enrollee vectors for each uniqueInsurance?

struct enrollee
{
    string userid = "";
    string fname = "";
    string lname = "";
    string insurance = "";
    string version = "";
};

struct uniqueInsurance
{
    string name = "";
    int numEnrollees = 0;
    vector <enrollee> enrollVector;
};
Blastfurnace
  • 18,411
  • 56
  • 55
  • 70
  • overload `operator<` and use `std::sort()` or just pass a sorting predicate to `std::sort()` – Cruz Jean Jun 30 '19 at 23:29
  • How are you currently trying to sort the data? (You presented the data structures, which is good, but not the code you are using to process them.) Also, are you required to use `vector`s for this? (There are other structures, like `map` and `set`, that may be better suited for storing sorted data.) – JaMiT Jul 01 '19 at 00:49

2 Answers2

1

If your task is just to write to different files sorted names then you do not need the second structure. Just have one std::vector<enrollee> sort it based on insurance and names, then iterate over it. When insurance name changes reopen file accordingly:

std::vector<enrollee> enrollees;
// read them from csv file
std::sort( enrollees.begin(), enrollees.end(), []( const enrollee &e1, const enrollee &e2 ) {
    return std::tie( e1.insurance, e1.fname, e1.lname ) < std::tie( e2.insurance, e2.fname, e2.lname );
} );
std::string insurance;
std::ofstream out;
for( const auto &e : enrollees ) {
    if( insurance != e.insurance ) {
        insurance = e.insurance;
        out.open( insurance + ".csv" );
    }
    out << e.fname << ',' << e.lname << std::endl;
}

This sorts by first name then last name, if you need last name first just swap their order in std::tie()

Slava
  • 43,454
  • 1
  • 47
  • 90
  • You are completely right, I was overcomplicating the problem quite a bit, which got me lost quick, but now that I see and follow your logic the answer seems so easy now. Thanks so much! – nikolai009 Jul 01 '19 at 01:03
  • Follow up question: it seems that my program sorts everything correctly, but when I try to write it to their respective files using that ranged for loop, it ONLY produces output for the Aetna enrollees and nothing else, but the logic seems to make perfect sense to me. Are the other files getting overwritten somehow? – nikolai009 Jul 01 '19 at 01:47
  • @nikolai009 check if `open()` succeded or not. If not check `errno`. If problem persists create another question with [mcve] – Slava Jul 01 '19 at 13:27
0

This is a follow up of your question yesterday:

Original question

I modified my code and added one line for sorting.

Please see:

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>


struct Enrollee
{
    // Data
    std::string userid{};
    std::string fname{};
    std::string lname{};
    std::string insurance{};
    std::string version{};

    // Overload Extractor Operator to read data from somewhere
    friend std::istream& operator >> (std::istream &is, Enrollee& e) {
        std::vector<std::string> wordsInLine{};       // Here we will store all words that we read in onle line;
        std::string wholeLine;                        // Temporary storage for the complete line that we will get by getline
        std::regex separator("[ \\;\\,]"); ;          // Separator for a CSV file
        std::getline(is, wholeLine);                  // Read one complete line and split it into parts
        std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1), std::sregex_token_iterator(), std::back_inserter(wordsInLine));
        // If we have read all expted strings, then store them in our struct
        if (wordsInLine.size() == 5) {
            e.userid = wordsInLine[0];
            e.fname = wordsInLine[1];
            e.lname = wordsInLine[2];
            e.insurance = wordsInLine[3];
            e.version = wordsInLine[4];
        }
        return is;
    }

    // Overload Inserter operator. Insert data into output stream
    friend std::ostream& operator << (std::ostream& os, const Enrollee& e) {
        return os << "userid is:    " << e.userid << "\nfname is:     " << e.fname << "\nlname is:     " << e.lname << "\ninsurance is: " << e.insurance << "\nversion is:   " << e.version << '\n';
    }
};


int main()
{
    // Her we will store all Enrollee data in a dynamic growing vector
    std::vector<Enrollee> enrollmentData{};

    // Define inputFileStream and open the csv
    std::ifstream inputFileStream("r:\\input.csv");

    // If we could open the file
    if (inputFileStream) 
    {

        // Then read all csv data
        std::copy(std::istream_iterator<Enrollee>(inputFileStream), std::istream_iterator<Enrollee>(), std::back_inserter(enrollmentData));

        // Sort the data
        std:sort(enrollmentData.begin(),enrollmentData.end(),[](const Enrollee& left, const Enrollee& right){return left.lname < right.lname;});

        // For Debug Purposes: Print all data to cout
        std::copy(enrollmentData.begin(), enrollmentData.end(), std::ostream_iterator<Enrollee>(std::cout, "\n"));
    }
    else {
        std::cerr << "Could not open file 'input.csv'\n";
    }
}
A M
  • 14,694
  • 5
  • 19
  • 44