How to initializing std::set<std::string> correctly? - c++

Please help me, I have been trying to do this for the past two-three hours, all with no luck. I have a number of strings comming in form input.txt in the format
string1 string2
string3 string4
etc.
that I want to put into a std::set which is initially empty. I want to number the strings as they come in and put them into the set to keep track of the duplicates so I don't number them again. I am trying to initialize std::set<std::string> inGraph but can't make it work. I tried to initialize std::set<std::string> inGraph(0, tot_lines); where 0 to tot_lines is the range of the number of total strings I expect to get form the input. The I tried to initialize all with empty stirng like: std::set<std::string> inGraph(tot_lines, ""); and that failed. Here's what I have now:
struct StringInt {
std::string name; // associate name and number for each input string
int number;
};
int main(int argc, char* argv[]) {
int tot_lines = 100;
int icv1, icv2;
std::string vert1, vert2;
std::set<std::string> inGraph(); // this is the set I want to initialize
std::set<std::string>::iterator sit;
std::vector<StringInt> stringInts(tot_lines*2);
StringInt* si;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2 >> vert1 >> vert2) {
// read in input, put it in vars below
myfile2 >> vert1 >> vert2;
if (inGraph.find(vert1) != inGraph.end()) {
icv1 = i++;
si->name = vert1;
si->number = icv1;
inGraph.insert(vert1);
stringInts.push_back(*si);
}
else {
icv1 = si->number;
}
if (inGraph.find(vert2) != inGraph.end()) {
icv2 = i++;
si->name = vert1;
si->number = icv2;
inGraph.insert(vert2);
stringInts.push_back(*si);
}
else {
icv2 = si->number;
}
}
The error I get is: left of '.find' must have class/struct/union Can you please help me figure out how to initialize the std::set<std::string> inGraph so I can number the strings?

The error message is because you are a victim of Most Vexing Parse.
std::set<std::string> inGraph();
It is a function declaration whose return type is std::set<std::string>. Just remove the () after inGraph to make it a object declaration.

Related

Problem in my C++ function with vector size not increasing

In the following code, I am attempting to add elements to a vector of strings, ints, and doubles but when I output the vector's size, it never moves past 1. This leads me to believe it's not adding elements, but instead changing the first element?
// Need to show this for the code I'm having issues with
struct Store_Info{ // Stores all info for a given item
string store_name;
string location;
// vector<string> = item | vector<int> = stock || vector<double> = price
pair<pair<vector<string>, vector<int>>, vector<double>> item_stock_price;
Store_Info() = default;
Store_Info(string, string);
string how_many(int);
};
void stock_info(vector<Store_Info> &stores, int n_stores){ // This is the code I need help with
for (int i(0); i<n_stores; i++){
string name; string loc;
int counter(0);
bool active(true);
while(active){
string line;
std::getline (cin,line);
if (line == "")
active = false;
else if (counter == 0){
name = line;
counter++;
}
else if (counter == 1){
loc = line;
stores[i] = Store_Info(name, loc);
counter ++;
}
else{
regex reg{R"((\w+),(\d+),\W(\d+.\d+))"}; // From professor's piazza post
std::smatch m;
std::regex_match(line, m, reg);
Store_Info current_store = stores[i];
pair itemStock = std::get<0>(current_store.item_stock_price);
std::get<0>(itemStock).push_back(m[1].str()); // Defines item name
std::get<1>(itemStock).push_back(std::stoi(m[2].str())); // Defines amount in stock
std::get<1>(current_store.item_stock_price).push_back(std::stod(m[3].str())); // Defines price
//cout << std::get<1>(current_store.item_stock_price).capacity();
}
}
}
}
Sorry if this is formatted poorly, this is my first post.
Any help is appreciated, thank you!
edit: Might be helpful to know what's being inputted..
Using standard input, the function reads in this:
(int) Stores:
(Name of a store)
(A location)
(item name),(quantity),$(price)
Ex.)
2 Stores:
Local Grocery
California
Apples,2,$1.20
Mall
Michigan
Pizza,3,$4.00
Cake,1,$10.45
Please consider to change you code to something like this.
Using nested pair's is way too confusing. Then at least use std::tuple.
Also you need to use reference to the struct and not its copy !
struct Item {
string name; // Defines item name
int amount; // Defines amount in stock
double price; // Defines price
}
struct Store_Info { // Stores all info for a given item
string store_name;
string location;
// vector<string> = item | vector<int> = stock | vector<double> = price
vector<Item> items;
Store_Info() = default;
Store_Info(string, string);
string how_many(int);
};
void stock_info(vector<Store_Info> &stores, int n_stores){ // This is the code I need help with
for (int i(0); i<n_stores; i++){
string name; string loc;
int counter(0);
bool active(true);
while(active){
string line;
std::getline (cin,line);
if (line == "")
active = false;
else if (counter == 0){
name = line;
counter++;
}
else if (counter == 1){
loc = line;
stores[i] = Store_Info(name, loc);
counter ++;
}
else{
regex reg{R"((\w+),(\d+),\W(\d+.\d+))"}; // From professor's piazza post
std::smatch m;
std::regex_match(line, m, reg);
Store_Info &current_store = stores[i]; // need to be reference and not the copy !
// item name | amount | price
current_store.items.emplace_back(m[1].str(), std::stoi(m[2].str()), std::stod(m[3].str()));
}
}
}
}
Not sure about other bugs or problems because this code is not run-able.

Expected Primary-Expression before ']' token?

I'm trying to be able to call a function with the vector and for some reason it's saying "expected primary expression before ']'. The vector could hold any number of files, depending on the amount of numbers in myfile, so I'm not sure what I should put there.
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
using namespace std; // not recommended
double averageCalc(string[],int);
int main () {
double average;
string line;
ifstream myfile ("array_pgmdata.txt");
//int index = 0; // not needed
//string myArray[index]; // UB - if it even compiles, it's a VLA of size 0.
std::vector<std::string> myArray; // use this instead to be able to grow it
// dynamically
if (myfile) // open and in a good state
{
// while (! myfile.eof() ) // It'll not be eof when you've read the last line
// only when you try to read beynd the last line,
// so you'll add "line" one extra time at the end
// if you use that. Use this instead:
while(getline(myfile, line))
{
// myArray[index++] << line; // you have 0 elements in the array and
// can't add to it in any way
myArray.push_back(line);
}
}
else cout << "Unable to open file";
for(size_t idx=0; idx < myArray.size(); ++idx) {
std::cout << myArray[idx] << "\n";
}
average = averageCalc(myArray[], line); // error here
return 0;
}
double averageCalc(string nums[], int count)
{
int a, total, elements, averaged1, averaged2;
// string averaged2;
for(a = 0; a < count; a++)
{
total+=a;
elements++;
}
averaged1 = total / elements;
return averaged2;
}
There's a few problems here. Firstly, your function averageCalc expects a parameter of type string[] which is an array of strings. When you call the function, you are trying to pass it a std::vector<string>, which is not an array of strings, it is a class. Presumably, you would want to change your function to take in a vector, like so:
double averageCalc( const std::vector<string> & nums ); // no need for size now
The other issue you have is in calling your function. When you call it, you pass myArray[] as a parameter, which is the error you compiler is giving you. This is not valid syntax, you simply want to pass in myArray.
I think that the error occurs becase firstly you create the array with std::vector<std::string> myArray; so the data is string type but when you want to calculate the average value the function expects a value int, double etc. in order to perform math. Either change the string to int or use a function to convert it:
int main()
{
string s = "12345";
// object from the class stringstream
stringstream geek(s);
// The object has the value 12345 and stream
// it to the integer x
int x = 0;
geek >> x;
// Now the variable x holds the value 12345
cout << "Value of x : " << x;
return 0;
}

C++ Loading user input into vector

I am trying to create a command line app, where the user can type in commands and data, but I don't really get how istream_iterator is working, how can I get a whole input (until enter) into a vector? Right now it creates a new while loop on every word, that is not what is want.
int main(int argc, char* argv[])
{
string buffer;
//vector<string> vbuff;
CliHandler clihandler(argc, argv);
int state = clihandler.State();
while (state != CliHandler::STATE_EXIT) {
cout << ">>";
//Beolvasás
cin >> buffer;
stringstream sstream(buffer);
istream_iterator<string> begin(sstream);
istream_iterator<string> end;
vector<string> vbuff(begin,end);
copy(vbuff.begin(), vbuff.end(), std::ostream_iterator<string>(std::cout, "\n"));//test
//vbuff = vector<string>((istream_iterator<string>(cin)), istream_iterator<string>());
//copy(vbuff.begin(), vbuff.end(), std::ostream_iterator<string>(std::cout, "\n"));
switch(clihandler.State(vbuff[0])) {
// [command] [data1] [data2] ...
}
}
return 0;
}
Why don't you just use the argc and argv parameters? Something like this..(haven't tested)
int main(int argc, char* argv[])
{
vector<string> vbuff(argc);
for (int i = 0; i < argc; ++i)
{
vbuff[i] = argv[i];
}
// From here, you can use vbuff for your own purposes.
}
I m not very sure what u want(my poor english..), maybe you want to get input of the whole line until enter
I think you can use cin.getline
char mbuf[1024];
cin.getline(buffer,1024);
Based on your comment: "I am reading input interactively. exit command would leave the while loop and end the program"
You'd be better off getting that simple loop to work first, before trying to process the input string.
std::string inputCommand;
while(inputCommand != "Exit")
{
cin >> inputCommand;
//do stuff with it
}
Then you could consider splitting and handling the string
bool shouldExit(false);
std::vector<std::string> inputsReceived;
while(!shouldExit)
{
char delim ('#'); //here put whatever character your inputs are separated by
std::string buffer;
cin >> buffer;
std::stringstream ss;
ss << buffer;
std::string item;
while (std::getline(ss, item, delim))
{
if (item == "Exit") //case sensitive
{
shouldExit = true;
break;
}
else
{
//do whatever with input
}
//if you want to keep a record of the inputs in a vector
inputsReceived.push_back(item);
}
}

How to number input in ascending order?

I have input coming in form a file input.txt as two columns of strings such as:
string1 string2
string3 string4
etc.
I am trying to number the strings in ascending order starting form 0 but in such a way that repeating strings don't get assigned new values but keep the once already assigned to them.
I decided to use a set::find operation to do this, but I am having a hard time making it work. Here's what I have so far:
int main(int argc, char* argv[]) {
std::ifstream myfile ("input.txt");
std::string line;
int num = 0; // num is the total number of input strings
if (myfile.is_open()) {
while(std::getline(myfile, line)) {
++num;
}
}
std::string str1, str1; // strings form input
int str1Num, str2Num; // numbers assigned to strings
int i = 0; // used to assign values to strings
StringInt si;
std::vector<StringInt> saveStringInts(num);
std::set<std::string> alreadyCounted(num, 0);
std::set<std::string>::iterator sit;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2.good()) {
// read in input, put it in vars below
myfile2 >> str1 >> str2;
// if strings are not already assigned numbers, assign them
if ((*(sit = alreadyCounted.find(str1)).compare(str1) != 0) { // doesn't work
str1Num = i++;
alreadyCounted.insert(str1);
saveStringInts.push_back(StringInt(str1Num));
}
else {
str1Num = si->getNum(str1);
}
if ((*(sit = alreadyCounted.find(str2)).compare(str2) != 0) {
str2Num = i++;
alreadyCounted.insert(str2);
saveStringInts.push_back(StringInt(str2Num));
}
else {
str2Num = si->getNum(str2);
}
// use str1 and str2 in the functions below before the next iteration
}
}
Unfortunately, I tried other approaches and now completely stuck. If you know how to fix my code or can suggest a better way to accomplish my task, I would greatly appreciate your help.
You need to compare std::set<int>::iterator against the end() iterator of your set, rather than dereferencing the iterator and comparing its value against something! Actually, derferencing the end() iterator is undefined behavior:
if ((*(sit = alreadyCounted.find(str1)).compare(str1) != 0) // WRONG: don't do that!
should really be
if (alreadyCounted.find(str1) != alreadyCounted.end())
... and likewise for the other string. Personally, I would use a different technique, though: when insert()ing into a std::set<T>, you get back a pair of an iterator and an indicator whether the object was inserted. The latter together with the current set's size give the next value, e.g.:
bool result = alreadyCounted.insert(str1).second;
strNum1 = result? alreadyCounted.size() - 1: si->getNum(str1);

Set illegal indirection error?

I'm getting a "set illegal indirection error" but I can't figure out what I am doing wrong.
What I am trying to do is take input from input.txt file that has a form
string1 string2
string3 string4
etc.
And number the strings in ascending order without duplicates, then use them in some functions and store the values in struct StringInt. But I am getting this weird error and I don't know where to start fixing it.
struct StringInt {
std::string name; // associate name and number for each input string
int number;
};
int main(int argc, char* argv[]) {
tot_strings = 100;
std::string vert1, vert2;
std::set<std::string> inGraph(0, tot_strings); // this is the set in question
std::set<std::string>::iterator sit;
std::vector<StringInt> stringInts(tot_strings); // Edit: forgot to mention this initially
StringInt* si;
int icv1, icv2;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2 >> vert1 >> vert2) {
myfile2 >> vert1 >> vert2;
if (inGraph.find(vert1) != inGraph.end()) { // not sure if I do this correctly
icv1 = i++;
si->name = vert1;
si->number = icv1;
inGraph.insert(vert1); // here's the insert that might be
stringInts.push_back(*si); // causing of the error
}
else {
icv1 = si->number;
}
if (inGraph.find(vert2) != inGraph.end()) {
icv2 = i++;
si->name = vert1;
si->number = icv2;
inGraph.insert(vert2);
stringInts.push_back(*si);
}
else {
icv1 = si->number;
}
// use icv1 and icv2 as int inputs in functions below
}
Does what I have make sense and how can I go about fixing the "set illegal indirection error"?
Edited: included std::vector stringInts(tot_strings) that I had in my code but forgot to include in the example.