nikolai009
nikolai009

Reputation: 5

C++ Alphabetically Sorting Strings in Vectors of Vectors

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;
};

Upvotes: 0

Views: 634

Answers (2)

A M
A M

Reputation: 15277

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";
    }
}

Upvotes: 0

Slava
Slava

Reputation: 44258

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()

Upvotes: 1

Related Questions