Set giving extra element - c++

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

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.

Using sort() on specific Member of Struct after a sort()

My Code is sorting a vector<Struct> accordingly to the Points the Player gained in the Game. This works fine. Now I wanted to add that if similar points, it sorts by time. So that the Person with the highest Points, but lowest time is on Top. My Problem lays in sorting the vector<Struct> without destroying the sort I did before for the points. Here is my Code
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iomanip>
using namespace std;
struct Highscore{
string Name;
string Points;
string Time;
};
Highscore parseToStruct(string data);
vector<string> Data = { "Ale, 01, 16", "Test, 10, 84", "Leon, 07, 61", "Cristian, 04, 43", "Kira, 09, 74", "Thomas, 01, 14", "Cooool, 05, 21", "Hohoho, 06, 56", "Neuer, 10, 81" };
bool compareByPoints(const Highscore &P1, const Highscore &P2){
return (P1.Points > P2.Points) || (P1.Points == P2.Points && P1.Time < P2.Time);
}
vector<Highscore> sorting(){
vector<Highscore> Test(Data.size());
transform(Data.begin(), Data.end(), Test.begin(), parseToStruct);
sort(Test.begin(), Test.end(), compareByPoints);
//if I change compareByPoints with compareByTime it shows me the lowest time, but not in combination with Points
return Test;
}
Highscore parseToStruct(string data){
Highscore Style;
vector<string> parse;
stringstream s_stream(data);
while (s_stream.good()){
string substr;
getline(s_stream, substr, ',');
parse.push_back(substr);
}
Style.Name = parse.at(0);
Style.Points = parse.at(1);
Style.Time = parse.at(2);
return Style;
}
int main(){
size_t fieldWidth = 8;
vector<Highscore> HighscoreList;
HighscoreList = sorting();
cout << " _________________________________________________\n"
<< "| HIGHSCORE |\n"
<< "| NAME | POINTS | TIME (s) |" << endl;
for (const auto &Highscore : HighscoreList){
cout << "|"
<< setw(fieldWidth + 10) << left << Highscore.Name << " | "
<< setw(fieldWidth) << left << Highscore.Points << " | "
<< setw(fieldWidth + 8) << left << Highscore.Time << " | " << endl;
}
cout << "|_________________________________________________|" << endl;
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
cin.get();
return 0;
}
Edit:
Thanks to #Lala5th was the solution quite simple and so I updated my Problem with the solution!
You can use lambda as custom comparator and do both points and time comparision in one function.
std::sort(Test.begin(), Test.end(), [](const Highscore &P1, const Highscore &P2) {
if (P1.Points == P2.Points)
{
return P1.Time < P2.Time;
}
return P1.Points > P2.Points;
}
You just need to add a check within your compareByPoints function.
If the Points are identical, check for time.

To read FILE and store data in map<string, vector<string>> c++98

I have file bird.lst, I need to read file contents and store data in map<string, vector>, here the idea is bird name is store in string and those having some attribute values that needs to be stored in vector. please help
eventually map<string, vector> looks like below,
ex:
parrot.sh ----> eat yes
fly yes
file contents below of bird.lst
parrot.sh
eat yes
fly yes
pigeon.sh
eat yes
fly yes
duck.sh
eat yes
fly no
flammingo.sh
eat yes
fly yes
eagle.sh
eat yes
flay yes
You need a nested loop.
The outside one reads the name of the bird (the key of the map)
The inside one reads the attributes of the bird (the values of the vector)
Here is what I came up with:
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
typedef std::vector<std::string> attribute_vector;
typedef std::map<std::string,attribute_vector> bird_map;
int main()
{
std::ifstream file("bird.lst");
bird_map birds;
std::string key;
while(std::getline(file,key))
{
attribute_vector attributes;
std::string value;
while(std::getline(file,value))
{
// in case it has windows encoding with end-of-line = \r\n
if (!value.empty() &&
value[value.size()-1] == '\r')
{
value.erase(value.size() - 1);
}
// if we found the empty string
if(value.empty())
{
break;
}
// save the value into the vector
attributes.push_back(value);
}
// save the bird into the map
birds[key] = attributes;
}
// now print the data we collected
for(bird_map::iterator bird = birds.begin();
bird != birds.end();
bird++)
{
std::cout << bird->first << "\n";
for(attribute_vector::iterator attribute = bird->second.begin();
attribute != bird->second.end();
attribute++)
{
std::cout << " " << *attribute << "\n";
}
std::cout << "\n";
}
return 0;
}
Try it at https://onlinegdb.com/1TBobUxE2 (it says C++17 as the compiler type but in the config under "Extra Compiler Flags" I am passing -std=c++98)
If you want to split the attribute from the yes/no value then:
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <utility>
typedef std::pair<std::string,std::string> attribute_pair;
typedef std::vector<attribute_pair> attribute_vector;
typedef std::map<std::string,attribute_vector> bird_map;
int main()
{
std::ifstream file("bird.lst");
bird_map birds;
std::string key;
while(std::getline(file,key))
{
attribute_vector attributes;
std::string value;
while(std::getline(file,value))
{
// in case it has windows encoding with end-of-line = \r\n
if (!value.empty() &&
value[value.size()-1] == '\r')
{
value.erase(value.size() - 1);
}
// if we found the empty string
if(value.empty())
{
break;
}
// now split the value into an attribute and a flag
attribute_pair attribute;
std::istringstream ss(value);
ss >> attribute.first >> attribute.second;
// save the value into the vector
attributes.push_back(attribute);
}
// save the bird into the map
birds[key] = attributes;
}
// now print the data we collected
for(bird_map::iterator bird = birds.begin();
bird != birds.end();
bird++)
{
std::cout << bird->first << "\n";
for(attribute_vector::iterator attribute = bird->second.begin();
attribute != bird->second.end();
attribute++)
{
std::cout << " " << attribute->first
<< " = " << attribute->second
<< "\n";
}
std::cout << "\n";
}
return 0;
}
Try it at https://onlinegdb.com/Htlh4eHu9 (it says C++17 as the compiler type but in the config under "Extra Compiler Flags" I am passing -std=c++98)
(Issue) The problem is not very specific.
So I'll try to make a very general solution.
Define a function:
bool is_file(string s) {
return (s.substr((int)s.size() - 3, 3) == ".sh");
}
Telling you if a string is a bird file.
Because of (Issue), I'll asume each attribute of the bird is of the form: (attribute, yes/no), wich we'll transform to (attribute, 1/0).
Now, to read the file, you have several options. I'll name two of them.
Pass the file from console, i.e, if you .exe is called birds.exe, just do birds.exe<bird.lst. And just read with std::cin.
Use freopen("bird.lst", "r", stdin); and just read with std::cin.
Then, the main function should look like this:
int main () {
freopen("bird.lst", "r", stdin); // if you didnt read from console.
map<string, vector<pair<string, bool>>> birds;
string current_bird;
while (cin >> s) {
string s;
cin >> s;
if (is_file(s)) {
current_bird = s;
continue;
}
bool verdict;
cin >> verdict;
bird[current_bird].push_back(make_pair(s, verdict));
}
}
And to print the data:
for (auto it = birds.begin(); it != birds.end(); it++) {
cout << "Bird File: " << it.first << "\n";
cout << "Attributes:\n";
for (auto x : it.second) cout << x.first << " " << (x.second ? "YES" : "NO") << "\n";
}

How to save unknown size structure (for later retrieval)

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

Using to upper incorrectly? Working code until I entered toupper

My program worked like it was supposed to until I added the toupper part into my program. I've tried looking at my error code but it's not really helping. The errors are:
no matching function to call
2 arguments expected, one provided
So I know the error is in those two statements in my while loop. What did I do wrong?
I want to make a name like
john brown
go to
John Brown
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
using namespace std;
int main(){
string firstname[5];
string lastname[5];
ifstream fin( "data_names.txt" );
if (!fin) {
cout << "There is no file" << endl;
}
int i = 0;
while( i < 5 && (fin >> firstname[i]) && (fin >> lastname[i]) ) {
firstname[0] = toupper(firstname[0]);
lastname[0] = toupper(lastname[0]);
i++;
}
cout << firstname[0] << " " << lastname [0] << endl;
cout << firstname[1] << " " << lastname [1] << endl;
cout << firstname[2] << " " << lastname [2] << endl;
cout << firstname[3] << " " << lastname [3] << endl;
cout << firstname[4] << " " << lastname [4] << endl;
return 0;
}
std::toupper works on individual characters, but you are trying to apply it to strings. Besides adding #include <cctype>, you need to modify your while loop's body:
firstname[i][0] = toupper(firstname[i][0]);
lastname[i][0] = toupper(lastname[i][0]);
i++;
Then it should work as expected. Live demo here
As M.M helpfully pointed out in the comments, you should also check that your strings aren't empty before accessing their first characters, i.e. something like
if (!firstname[i].empty()) firstname[i][0] = toupper(...);
is strongly recommended.
Mind you, you will probably need more sophisticated logic if you get names like McDonald :)
You need ctype.h to get the proper definition for toupper(). It is usually implemented not as a function, but an array mapping.
#include <ctype.h>
The program has several flaws: using a string array instead of a string, not iterating through the string correctly, not declaring but using the C definition of toupper(), not exiting when the file does not exist.
Use this instead:
#include <ctype.h>
#include <iostream>
#include <string>
using namespace std;
int main ()
{
ifstream fin ("data_names.txt");
if (!fin)
{
cerr << "File missing" << endl;
return 1;
}
// not sure if you were trying to process 5 lines or five words per line
// but this will process the entire file
while (!fin.eof())
{
string s;
fin >> s;
for (i = 0; i < s.length(); ++i)
s [i] = toupper (s [i]);
cout << s << endl;
}
return 0;
}