u23 u23 - 25 days ago 7
C++ Question

C++ : Adding rows to a vector of vectors

I'm trying to mimic some of the dataframe functionality from R in C++, i.e., read from a CSV file into a matrix and add/remove rows. The number of rows in the CSV file can be anything, BUT the number of columns and their datatypes are fixed. So it's not supposed to be too general-purpose (i.e. variable number of columns or variable datatypes for columns). I've been able to make a basic program that reads data to a vector of string vectors.

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>

using namespace std;

using vec = vector<string>;
using matrix = vector<vec>;


matrix readCSV(string filename)
{
char separator = ',';
matrix result;
string row, item;

ifstream in(filename);
while(getline(in, row))
{
vec R;
stringstream ss(row);
while(getline(ss, item, separator)) R.push_back( item );
result.push_back(R);
}
in.close();
return result;
}



void printMatrix(const matrix &M)
{
for(vec row : M)
{
for (string s:row) cout << setw( 12 ) << left << s << " ";
cout << '\n';
}
}



void deleteRow(matrix &M, int row)
{
if(row < M.size()) M.erase( M.begin() + row );
}



void deleteCol(matrix &M, int col)
{
for(vec &row : M) if ( col < row.size() ) row.erase( row.begin() + col );
}



void edit( matrix &M, int i, int j, string value )
{
if (i < M.size() && j < M[i].size()) M[i][j] = value;
}



int main()
{
matrix pets = readCSV( "pets.csv" );
printMatrix( pets );

cout << "\n\n";

deleteRow( pets, 3 );
deleteCol( pets, 3 );
edit( pets, 1, 2, "12" );
printMatrix( pets );
}


pets.csv:

Animal,Name,Age,Food,Owner,Notes
Dog,Fido,6,Chewies,R. Smith,Barks
Cat,Fluffy,8,Whiskers,M. Jones,Miaows
Hamster,Giles,2,Scratchies,A. Green
Snake,Hissie,3,Mice,Bob


Output:

Animal Name Age Food Owner Notes
Dog Fido 6 Chewies R. Smith Barks
Cat Fluffy 8 Whiskers M. Jones Miaows
Hamster Giles 2 Scratchies A. Green
Snake Hissie 3 Mice Bob


Animal Name Age Owner Notes
Dog Fido 12 R. Smith Barks
Cat Fluffy 8 M. Jones Miaows
Snake Hissie 3 Bob


The main issue is that all columns are of the same datatype (in this case, string). What modifications should I make to allow columns of different data types (e.g. in this case, the age columns should be int and the rest string)?
Also, how do I add new rows or columns to the matrix?

Answer Source

If each column is a different type, that implies you should have a vector of objects, not a vector of vectors. You're not dealing with tabular data, you're dealing with records.

Something like:

struct Pet
{
    std::string animal;
    std::string name;
    unsigned int age;
    std::string food;
    std::string owner;
    std::string notes;
};

// Later
std::vector<Pet> pets;

In this model:

how do I add new rows to the matrix?

Push a new Pet object into the vector.

how do I add new columns to the matrix?

Add a new data member to the Pet type.

how can I read data from the stringstream into the struct object

Use a helper function to do the reading:

Pet read_pet(std::string const &row)
{
    Pet pet;

    std::istringstream ss{row};

    std::getline(ss, pet.animal, ',');
    std::getline(ss, pet.name, ',');
    ss >> pet.age;
    ss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
    std::getline(ss, pet.food, ',');
    std::getline(ss, pet.owner, ',');
    std::getline(ss, pet.notes, ',');

    return pet;
}

and then push that into the matrix?

while(getline(in, row))
{
    result.emplace_back(read_pet(row));
}