How to save unknown size structure (for later retrieval) - c++

My plan is to store a couple dozen rows with 2 items per row and both items will have a different data type. Not sure if this is the right approach and have heard about using vectors but I can't find any samples that will take in 2 items with different types with many rows (an unknown amount of rows) similar to what I'm trying to do here. The following doesn't compile
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct movies_t {
string title;
int year;
} myRecNo[];
void printmovie (movies_t movie);
int main ()
{
string mystr;
for (int i=0; i < 2; i++)
{
switch (i)
{
case 1:
myRecNo[i].title = "2001 A Space Odyssey";
myRecNo[i].year = 1968;
cout << "Auto entry is:\n ";
printmovie (myRecNo[i]);
break;
case 2:
myRecNo[i].title = "2002 A Space Odyssey";
myRecNo[i].year = 1978;
cout << "Auto entry is:\n ";
printmovie (myRecNo[i]);
break;
}
}
return 0;
}
void printmovie (movies_t movie)
{
cout << movie.title;
cout << " (" << movie.year << ")\n";
}
This is the error I get:
Test1.obj||error LNK2019: unresolved external symbol "struct movies_t * myRecNo" (?myRecNo##3PAUmovies_t##A) referenced in function _main|

There are a couple of bad practices in your code, if you are just asking for a way to modify the program so that it will compile and work, see the following:
Declare struct and create struct variables in your main function.
struct movies_t
{
string title;
int year;
};
then, in your main function, movies_t myRecNo[2];
Arrays start at index 0, not 1. so your switch should be
switch (i)
{
case 0:
myRecNo[i].title = "2001 A Space Odyssey";
myRecNo[i].year = 1968;
cout << "Auto entry is:\n ";
printmovie(myRecNo[i]);
break;
case 1:
myRecNo[i].title = "2002 A Space Odyssey";
myRecNo[i].year = 1978;
cout << "Auto entry is:\n ";
printmovie(myRecNo[i]);
break;
}
// the rest of the code..
After you modify these, your code should work.
However, for a better data structure to save an array of paired values, you can use std::vector<std::pair<string, int>> myReg to save your data.
the following code should be much much better, remember to #include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
void printmovie(std::vector<std::pair<std::string, int>>);
int main()
{
std::vector<std::pair<std::string, int>> myReg;
myReg.push_back({ "2001 A Space Odyssey", 1968 });
myReg.push_back({ "2002 A Space Odyssey", 1978 }); // <- if your compiler is not using c++11 standard or above, please change this line to myReg.push_back(std::pair<std::string, int>("name of the movie", int)); to use to older version of Initializer
printmovie(myReg);
return 0;
}
void printmovie(std::vector<std::pair<std::string, int>> movie)
{
for (auto itor = movie.begin(); itor != movie.end(); ++itor)
{
//first is the first data in the pair, which is the title
//second is the second data in the pair, which is the year
std::cout << (*itor).first << " (" << (*itor).second << ")\n";
}
}

Thanks everyone & #Zhou.
Zhou's code above might work on a newer version of the compiler but I'm using Code::Blocks IDE with MS Visual C++ 2010 compiler.
Here is the vector method that worked:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
void printmovie(std::vector<std::pair<std::string, int>>);
int main()
{
std::vector<std::pair<std::string, int>> myReg;
myReg.push_back(std::pair<std::string, int>("title of the movie", 1968));
myReg.push_back(std::pair<std::string, int>("title of the movie2", 1978));
//myReg.push_back({ "2001 A Space Odyssey", 1968 });
//myReg.push_back({ "2002 A Space Odyssey", 1978 });
printmovie(myReg);
//or to print a single element (the 2nd row) thanks #zhou
std::cout << myReg[1].first << " " << myReg[1].second << std::endl;
return 0;
}
void printmovie(std::vector<std::pair<std::string, int>> movie)
{
for (auto itor = movie.begin(); itor != movie.end(); ++itor)
{
//first is the first data in the pair, which is the title
//second is the second data in the pair, which is the year
std::cout << (*itor).first << " (" << (*itor).second << ")\n";
}
}

Related

end_unique algorithm, element disappear

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
using namespace std;
void elimdups(vector<string>& words) {
sort(words.begin(), words.end());
for(auto a : words)
{
cout << a << " ";
}
cout << endl;
auto end_unique = unique(words.begin(), words.end());
for (auto a : words)
{
cout << a << " ";
}
cout << endl;
words.erase(end_unique, words.end());
for (auto a : words)
{
cout << a << " ";
}
cout << endl;
}
int main()
{
vector<string> kim = { "love", "peace", "horrible", "love", "peace", "hi", "hi" };
elimdups(kim);
return 0;
}
--
result:
hi hi horrible love love peace peace
hi horrible love peace love peace
hi horrible love peace
end_unique algorithm do not delete elements.
but on the second cout operation, "hi" disappear.
why "hi" disappear on the second line?
auto end_unique = unique(words.begin(), words.end());
for (auto a : words)
//...
Any items from [end_unique, words.end()) have unspecified values after the call to std::unique. That's why the output in the "erased" range seems strange.
If you want to preserve the "erased" words and keep the relative order, std::stable_partition with the appropriate lambda that checks duplicates could have been done.

Set giving extra element

I'm doing some rather simple code with set
#include <iostream>
#include <map>
#include <string>
#include <set>
using namespace std;
void printSol(map<string, string> parelles, const set<string>& sols) {
cout << "COUPLES:" << endl;
for (auto& x : parelles) {
cout << x.first << " " << x.second << endl;
parelles.erase(x.second);
}
cout << "ALONE:" << endl;
for (auto x : sols) {
cout << x << endl;
}
cout << "----------" << endl;
}
int main() {
map<string, string> parelles;
set<string> sols;
string inst, nom1, nom2;
while (cin >> inst) {
if (inst == "liats") {
cin >> nom1 >> nom2;
sols.erase(nom1);
sols.erase(nom2);
sols.insert(parelles[nom1]);
sols.insert(parelles[nom2]);
parelles.erase(parelles[nom1]);
parelles.erase(parelles[nom2]);
parelles[nom1] = nom2;
parelles[nom2] = nom1;
}
else if (inst == "info") {
printSol(parelles, sols);
}
}
}
For the input:
liats gerard shakira
liats sara iker
liats gerard sara
liats iker cristiano
info
It prints
COUPLES:
cristiano iker
gerard sara
ALONE:
shakira
----------
but should print
COUPLES:
cristiano iker
gerard sara
ALONE:
shakira
----------
But there is an extra endl after ALONE. I checked the size of the set and it's 2, and I don't really know what's going on. It seems like x has the null string.
Can someone point out in the right direction?
The map::operator[] strikes again. This operator inserts a value-initialized element if the key doesn't exist in the map. For string, this means it will insert an empty string. Here's a fix:
// sols.insert(parelles[nom1]);
// sols.insert(parelles[nom2]);
auto it = parelles.find(nom1);
if (it != parelles.end()) sols.insert(it->second);
it = parelles.find(nom2);
if (it != parelles.end()) sols.insert(it->second);

std::vector Struct Student example

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct StudentDataTypes
{
std::string name{};
int grade{};
};
int main()
{
//Ask for Class_Size
int Class_Size{};
std::cout << "How big is the class?" << '\n';
std::cin >> Class_Size;
//Syntax format
//std::vector<T> array(size);
//Intialize a vector for the students called Vector_Student
//T links to the struct StudentDataTypes
//size is the Class_Size.
std::vector<StudentDataTypes> Vector_Student(Class_Size);
//Print Class Size
std::cout << "There are " << Class_Size << " students." << '\n';
//Get the Userinputs for the Class
for (int i = 0; i < Class_Size; ++i)
{
std::cout << "Please input the name of Student #" << i + 1 << '\n';
std::cin >> Vector_Student[i].name;
std::cout << "Please input the grade of Student #" << i + 1 << '\n';
std::cin >> Vector_Student[i].grade;
}
//Sort
std::sort(Vector_Student.begin(), Vector_Student.end());
//Print the required output
for (int j = 0; j < Class_Size; ++j)
{
std::cout
<< Vector_Student[j].name
<< " got a grade of "
<< Vector_Student[j].grade << '\n';
}
return 0;
}
I have an issue with a Vector Struct tutorial.
I'm using Visual Studio 2019 and there's a peculiar scenario where the compiler doesn't give me any warning at all. If I debug it, the first warning appears on line 1544, way out of bounds. The above code will actually sort of compile, run and crash.
I know the issue lies in the sorting but I can't figure it out.
std::sort requires you to implement operator< for your datatype. Here in this case adding following definition in your class will get your code to compile
bool operator<(const StudentDataTypes& that) {
return this->grade < that.grade;
}
Update:
Alternatively as sugested by Casey, we can use custom sort comparator. Here is the sample code for the same.
std::sort(Vector_Student.begin(), Vector_Student.end(), [](const StudentDataTypes& a, const StudentDataTypes&b) { return a.grade < b.grade; });
Here is the answer. I'm posting it in the answers for how to sort a vector-struct.
Step 1:
bool compareTwoStudents(StudentDataTypes a, StudentDataTypes b) {
if (a.grade != b.grade)
return a.grade > b.grade;
return a.grade==b.grade;
}
Step 2:
std::sort(Vector_Student.begin(), Vector_Student.end(),compareTwoStudents);

No Instance of Overloaded function while using STL queues

Okay so I have a programming project due in about 7 minutes. My project reads a double GPA and a string Name in a txt file. I correctly place the GPA and Names into separate STL lists for the project. My problem comes when I try to push the highest GPA value into a queue. At that point it gives the error that no instance of overloaded function.
Since I am using pointers, I have tried different methods of using pointers but to no avail. I figure that pointers might be a part of the problem.
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <list>
#include <queue>
using namespace std;
//using std::because it says cout is ambiguous even with std included
int main()
{
double GPA;
double highestGPA;
string name;
list<double> listGPA; //lists and their iterators
//list<double>::iterator ptrGPA, ptrHighestGPA;
double *ptrGPA, *ptrHighestGPA;
list<string> listName;
//list<string>::iterator ptrName, ptrHighestName;
string *ptrName, *ptrHighestName;
queue<double> queueGPA; //queues
//queueGPA.front() = 0;
queue<string> queueName;
//queueName.front() = "";
ifstream infile;
infile.open("HighestGPAData.txt");
if (!infile) //if file doenst exist
{
std::cout << "The input file does not "
<< "exist. Program terminates!"
<< endl;
std::system("pause");
return 1;
}
std::cout << fixed << showpoint;
std::cout << setprecision(2);
infile >> GPA >> name;
while (infile) { //place contents of file into 2 STL lists
listGPA.push_back(GPA);
listName.push_back(name);
infile >> GPA >> name;
}
std::cout << "\nSTL list for GPA:\n";
for (auto g : listGPA) { //range based for loop that prints GPA list
std::cout << g << "\n";
}
std::cout << "\nSTL list for names:\n";
for (auto s : listName) { //range based for loop that prints name
std::cout << s << "\n";
}
std::system("pause");
ptrGPA = &listGPA.front(); //initialize pointers for each list
ptrHighestGPA = ptrGPA;
ptrName = &listName.front();
ptrHighestName = ptrName;
while (!listGPA.empty()) // there are two lists (name and GPA)
if (ptrGPA > ptrHighestGPA) {
while (!queueGPA.empty()) { //destroy queue i (so a lower
queueGPA.pop();
}
while (!queueName.empty()) { //destroy queue if a higher //gpa is found (so a lower gpa is not printed)
queueName.pop();
}
/***********************problem here */
ptrHighestGPA = ptrGPA;
queueGPA.push(ptrHighestGPA); //add to queue if it is
ptrHighestName = ptrName;
queueName.push(ptrHighestName);
}
else if (ptrGPA == ptrHighestGPA) { //add to the queue ifcurrent gpa
queueGPA.push(ptrHighestGPA);
queueName.push(ptrHighestName);
}
/* end problem */
listGPA.pop_front();
listName.pop_front();
ptrGPA++;
ptrName++; //= listName.front();
}
while (!queueGPA.empty()) { //print the largest gpa.
std::cout << queueGPA.front() << " ";
queueGPA.pop();
}
std::cout << "\n";
while (!queueName.empty()) { //print who has the highest gpa
std::cout << queueName.front() << " ";
queueName.pop();
}
std::system("pause");
return 0;
}
Currently it outputs both lists but I can easily comment that out. I want the queues queueName and queueGPA to contain the highest GPA and name associated with that GPA from the lists listGPA and listName.
Thank you guys

How to print words using reverse_iterator in STL

I have to read two text files and then compare words from second file with the first one. Then , I have to display KnownWords which are same words from both files and the remaining words which are not same are UnknownWords. Next Step is, I have to display most frequent known words in DisplayMostFreqKnownWords() and unknown words in DisplayMostFreqUnknownWords() functions. I have successfully completed DisplayMostFreqKnownWords() and so far Output is alright. I copied the same code from DisplayMostFreqKnownWords() to DisplayMostFreqUnknownWords() but in this is function it is not showing anything in the output. I dont know what is wrong. Can someone figure this one out.
Output is:
Displaying most frequent known words
Word Count
the 19
a 14
of 11
artificial 11
that 10
to 7
signal 7
and 7
in 6
they 5
Displaying most frequent unknown words
Word Count
Header file:
typedef map<string, vector<int> > WordMap;
typedef WordMap::iterator WordMapIter;
class WordStats
{
public:
WordStats();
void ReadDictionary();
void DisplayDictionary();
void ReadTxtFile();
void DisplayKnownWordStats();
void DisplayUnknownWordStats();
void DisplayMostFreqKnownWords();
void DisplayMostFreqUnknownWords();
private:
WordMap KnownWords;
WordMap UnknownWords;
WordMapIter Paragraph;
set<string> Dictionary;
char Filename[256];
}
My program:
// Displays 10 most frequent words in KnownWords
void WordStats::DisplayMostFreqKnownWords(){
int count;
multimap<int,string > displayFreqWords;// new map with int as key
(multimap because key could occur more than once)
multimap<int,string >::reverse_iterator rit = displayFreqWords.rbegin();
for (Paragraph = KnownWords.begin(); Paragraph != KnownWords.end();
++Paragraph){ // iterate map again
string word = (*Paragraph).first;
int cnt = (*Paragraph).second.size();
displayFreqWords.insert(pair<int,string>(cnt,word));
}
// multimap<int,string>::iterator rit; // iterator for new map
cout <<" Word Count\n";
for(; count<=10 && rit!=displayFreqWords.rend(); rit++, ++count){
string word = (*rit).second;
int cnt = (*rit).first;
cout << setw(15) << word << setw(10) << cnt << endl;
}
}
// Displays 10 most frequent words in UnknownWords
void WordStats::DisplayMostFreqUnknownWords(){
int count;
multimap<int,string > displayFreqUnknownWords;
multimap<int,string >::reverse_iterator rrit =
displayFreqUnknownWords.rbegin();
for (Paragraph = UnknownWords.begin(); Paragraph !=
UnknownWords.end(); ++Paragraph){
string word = (*Paragraph).first;
int cnt = (*Paragraph).second.size();
displayFreqUnknownWords.insert(pair<int,string>(cnt,word));
}
// multimap<int,string>::iterator rit; // iterator for new map
cout <<" Word Count\n";
for(; count<=10 && rrit!=displayFreqUnknownWords.rend(); rrit++, ++count){
string wrd = (*rrit).second;
int ccnt = (*rrit).first;
cout << setw(15) << wrd << setw(10) << ccnt << endl;
}
}
Here's a way to express what I think is your use case. I have used c++17 tuple expansion.
I have used unordered_map to deduce which words are known or unknown, and two multimaps to determine known and unknown word frequency.
Hope it's helpful.
#include <sstream>
#include <tuple>
#include <string>
#include <unordered_map>
#include <algorithm>
#include <iterator>
#include <map>
#include <iostream>
#include <iomanip>
#include <fstream>
// Set this to 1 to run a static test
#define TESTING 0
#if TESTING
using input_type = std::istringstream;
std::tuple<input_type, input_type> open_inputs() {
return {
std::istringstream("the big black cat sat on the grey mat"),
std::istringstream("the gold small cat lay on the purple mat")
};
}
#else
using input_type = std::ifstream;
std::tuple<input_type, input_type> open_inputs() {
return {
std::ifstream("left_file.txt"),
std::ifstream("right_file.txt"),
};
}
#endif
struct Counts {
int left_count = 0, right_count = 0;
int total() const {
return left_count + right_count;
}
bool is_known() const {
return left_count && right_count;
}
};
template<class F>
void for_each_word_in_file(std::istream &is, F f) {
std::for_each(std::istream_iterator<std::string>(is),
std::istream_iterator<std::string>(),
f);
}
int main() {
// open files
auto[left, right] = open_inputs();
auto known_words = std::unordered_map<std::string, Counts>();
// count words in each file
for_each_word_in_file(left, [&known_words](auto &&word) {
++known_words[word].left_count;
});
for_each_word_in_file(right, [&known_words](auto &&word) {
++known_words[word].right_count;
});
// map counts to words, in descending order, allowing multiple entries of the same count
std::multimap<int, std::string, std::greater<>> known_ordered, unknown_ordered;
// iterate all words seen, putting into appropriate map
for (auto&&[word, counts] : known_words) {
(counts.is_known() ? known_ordered : unknown_ordered)
.emplace(counts.total(), word);
}
// emit results
std::cout << "Known words by frequency\n";
for (auto&&[freq, word] : known_ordered) {
std::cout << std::setw(15) << word << " " << freq << '\n';
}
std::cout << "\nUmknown words by frequency\n";
for (auto&&[freq, word] : unknown_ordered) {
std::cout << std::setw(15) << word << " " << freq << '\n';
}
}