How to remove certain specified value from vectors? - c++

mySongs is a vector that store a collection of songs input by the user. In the if statement, the program will check the elements in the vector with user input. If match, it will delete that specified value from the vector. When I look for the solution, I see someone recommend to use remove/erase idiom:. But when I implement in my code, it continue pop up this error C2678 binary '==': no operator found which takes a left - hand operand of type 'Song' (or there is no acceptable conversion)
void deleteSong() {
string songTitle;
cout << "\n\t\tPlease enter the particular song name to remove: ";
cin >> songTitle;
if (songTitle != "") {
for (Song songs : mySongs) {
if (songs.title == songTitle) {
mySongs.erase(find(mySongs.begin, mySongs.end, songs)); //erase an element with value
break;
}
}
}
}

This error is due to the fact that the class Song does not have an == operator. This can be solved in one of the two following ways
If you have access to the source code of Song then
add the following function to it. please note that const is necessary
bool operator == ( const Song& song)
{
//Do the comparison code here
}
If you don't have access to the source code. Then add the following function
bool operator == (const Song& song1, const Song& song2)
{
// Do the comparison code here
}
That said, there is another minor problem with your code
The erase function should be called like this
mySongs.erase(find(mySongs.begin(), mySongs.end(), songs));
I decided to add a minimal example to help you. In that example I assumed a certain implementation of Song, but that implementation does not have to be what you have
#include <iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
class Song
{
public:
std::string title;
/* bool operator ==(const Song& other)
{
return this->title == other.title;
}*/
};
bool operator ==(const Song& song1,const Song& other)
{
return song1.title == other.title;
}
std::vector<Song> mySongs={{"ali"},{"ahmed"},{"ali"}};;
void deleteSong() {
string songTitle;
cout << "\n\t\tPlease enter the particular song name to remove: ";
cin >> songTitle;
if (songTitle != "") {
for (const auto& songs : mySongs) {
if (songs.title == songTitle) {
mySongs.erase(find(mySongs.begin(), mySongs.end(), songs)); //erase an element with value
break;
}
}
}
}
int main() {
// your code goes here
return 0;
}

Related

How to output enum elements using cout?

I am trying to output a element of enum I declared but for example, when I input push_ups, it outputs a number like 8621623 instead of showing push_ups. I have no idea why. I am Japanese so I am sorry if my English is broken. Thank you so much.
#include <iostream>
#include <string>
using namespace std;
enum exercise { push_ups, sit_ups, squats, walking, radio_calisthenics };
istream& operator>>(istream& is, exercise& i)
{
int tmp;
if (is >> tmp)
i = static_cast<exercise>(tmp);
return is;
}
int main()
{
exercise a;
cin >> a;
cout << a << endl;
}
I am trying to output a element of enum I declared but for example, when I input push_ups, it outputs a number like 8621623 instead of showing push_ups.
In the operator>> overload, std::cin accepts integers so push_ups isn't an integer, so std::cin will fail and and the line i = static_cast<exercise>(tmp); will be skipped making a uninitialized which when printed can cause Undefined Behavior to occur.
If you want to map strings to respective enum values, you could do that by mapping each string to the corresponding enum values manually using a hashmap (In C++, that container is called std::unordered_map):
#include <unordered_map>
// ...
const unordered_map<string, exercise> exercise_map {
{ "push_ups", push_ups },
{ "sit_ups", sit_ups },
{ "squats", squats },
{ "walking", walking },
{ "radio_calisthenics", radio_calisthenics }
};
istream& operator>>(istream& is, exercise& i) {
std::string tmp;
if (is >> tmp) {
auto const it = exercise_map.find(tmp);
if (it != exercise_map.end())
i = it->second;
}
return is;
}
Now, to print out the corresponding string value from the enum, we have to do the reverse, i.e., find the key in the hashmap using the value:
ostream& operator<<(ostream& os, exercise& i) {
auto const it = std::find_if(exercise_map.begin(), exercise_map.end(),
[i](std::pair<std::string, exercise> const& e) {
return e.second == i;
}
);
if (it != exercise_map.end())
os << it->first;
return os;
}
This is how the full code should look like:
#include <unordered_map>
#include <algorithm>
#include <utility>
#include <iostream>
#include <string>
using namespace std;
enum exercise { push_ups, sit_ups, squats, walking, radio_calisthenics };
const std::unordered_map<std::string, exercise> exercise_map {
{ "push_ups", push_ups },
{ "sit_ups", sit_ups },
{ "squats", squats },
{ "walking", walking },
{ "radio_calisthenics", radio_calisthenics }
};
istream& operator>>(istream& is, exercise& i) {
std::string tmp;
if (is >> tmp) {
auto const it = exercise_map.find(tmp);
if (it != exercise_map.end())
i = it->second;
}
return is;
}
ostream& operator<<(ostream& os, exercise& i) {
auto const it = std::find_if(exercise_map.begin(), exercise_map.end(),
[i](std::pair<std::string, exercise> const& e) {
return e.second == i;
}
);
if (it != exercise_map.end())
os << it->first;
return os;
}
int main() {
exercise a;
cin >> a;
cout << a << endl;
}
push_ups is not a valid integer value that you are trying to read at is >> tmp so a remains uninitialized. If you want to input names then you'll need to read string and then manually convert it to corresponding enum value. Same for output. Without a properly overloaded operator << a will be treated as an integer.
this line here:
i = static_cast<exercise>(tmp);
will work perfectly when tmp is holding an int value between 0 and 4,
but in your case tmp = push_ups is breaking the cast operation and your ref i is wrongly initialized
You seem to have problems with understanding enums.
I think you entered the string value "push_ups" and assumed the program would understand it to refer to a value of your enum.
An enum is just a placeholder for an integer type, mostly used to increase the readability of your program.
See an enum to be a better way to express something like
// chess_piece = 1 : pawn
// chess_piece = 2 : rook
// chess_piece = 3 : bishop
...
int chess_piece;
as
enum ChessPiece { Pawn, Rook, Bishop, ...};
ChessPiece chess_piece;
In the upper variant, it is clear that the names pawn, rook etc are comment only. The enum isn't different in that regard. It is clearly more readable to write if(chess_piece == Pawn), but the word "Pawn" is only a part of the code in the programming language, not in the compiled program. It does not exist as string value.
What you can do is to add something like
exercise exercise_from_string(const std::string& input)
{
if(input == "push_ups") return push_ups;
...
Or the same with a switch. I'd also advise that you have a value "unknown" in case a function like that finds no known term.
Edit: One of the other guys updated his answer while I wrote this, his code using a std::map is better than mine.
Hope my answer helps you understanding the core issue, though.

How to overload == operator to see if two objects with a string vector are equal?

I am writing a class named StringSet which has vector<string> data and int length as its private members.
bool StringSet::operator == (StringSet d)
{
for (int i = 0; i < length; i++)
{
if (data[i] == d.data[i])
{
return true;
}
}
return false;
}
When I try calling this function like this,
StringSet doc1, doc2;
if (doc1 == doc2)
{
cout << "Both sentences are identical!\n";
}
I get an assertion failure saying vector subscript out of range, I know what that means but I don't know how it implies here. If anyone can point out an obvious mistake I have made that would be great as I am a newbie to c++.
It's simple
bool StringSet::operator == (const StringSet& d) const
{
return data == d.data;
}
std::vector and std::string have already comparison operators, therefore you don't have to implement something special.

Sorting of set of objects is incorrect

When I print the whole set, the result is unsorted AND it contains one duplicate.
The object Person has a surname, a familyname and year of birth (all 3 are strings). I first sort by year of birth, then by familyname and then by surname. Per se, there are no identical persons (but even if was the case, it should be eliminated as they get inserted into the set ).
To be more concrete, I create a set of persons like this:
std::set <Person> greatUncles;
and insert them like this:
greatUncles.insert(Person("bla", "bla", "1900"));
Here's are the essential things from class Person:
class Person {
public:
//...
Person(std::string s, std::string f, std::string y)
:surname(s), familyname(f), yearOfBirth(y)
{
}
//...
std::string getSurname() const {
return surname;
}
std::string getFamilyname() const {
return familyname;
}
std::string getYearOfBirth() const {
return yearOfBirth;
}
private:
std::string surname;
std::string familyname;
std::string yearOfBirth;
};
//to print the set, overload the '<<' operator
std::ostream &operator<<(std::ostream &o, const Person &person) {
o << person.getSurname() << " "
<< person.getFamilyname() << " "
<< person.getYearOfBirth() << std::endl;
return o;
}
//to order the set, overload the '<' operator
bool operator< (Person const &p1, Person const &p2) {
int compareYearOfBirth = p1.getYearOfBirth().compare(p2.getYearOfBirth());
if (compareYearOfBirth == 0) {
int compareFamilyname = p1.getFamilyname().compare(p2.getFamilyname());
if (compareFamilyname == 0) {
return p1.getSurname().compare(p2.getSurname());
} else
return compareFamilyname;
} else
return compareYearOfBirth;
}
and here is how I print the set of great-uncles:
void printGreatUncles(std::set <Person> &greatUncles) {
std::ofstream outputFile;
outputFile.open("greatuncle.dat");
if (outputFile.is_open()) {
for(Person const & person:greatUncles) {
outputFile << person;
}
outputFile.close();
}
}
Now the output in a certain case should look like this (sorted by year):
Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967
but it looks like this:
Archibald Furtweger 1967
Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967
I can't figure it for my life what (things) I'm doing wrong.
std::set requires the comparator provides a strict weak ordering. Part of that is if a < b == true then b < a == false but you don't have this. Lets imagine the birth year and the family names are the same, and only the surnames are different. In you example you would return some positive or negative number which is converted to true since only 0 is false. If you run the check backwards then you get the opposite value in the integer, but it still results in true.
To fix this C++11 offers std::tie that you can use to build a std::tuple of the members and its operator < is built to do the right thing. That makes you code look like
bool operator< (Person const &p1, Person const &p2) {
return std::tie(p1.getYearOfBirth(), p1.getFamilyname(), p1.getSurname()) <
std::tie(p2.getYearOfBirth(), p2.getFamilyname(), p2.getSurname());
}
If you ever want to do this going forward and can use C++20 then you can add to Person
auto operator<=>(const Person&) const = default;
and that will automatically give you operators ==, !=, <, <=, >, and >= for Person and they will "do the right thing" as long as you want all members compared in the order they are defined in the class.
You are returning the int returned by std::string::compare as a bool. That's not what you want, as both 1 and -1 are converted to true.
The correct comparison code would be:
//to order the set, overload the '<' operator
bool operator< (Person const &p1, Person const &p2) {
int compareYearOfBirth = p1.getYearOfBirth().compare(p2.getYearOfBirth());
if (compareYearOfBirth == 0) {
int compareFamilyname = p1.getFamilyname().compare(p2.getFamilyname());
if (compareFamilyname == 0) {
return p1.getSurname().compare(p2.getSurname()) < 0;
} else
return compareFamilyname < 0;
} else
return compareYearOfBirth < 0;
}
The std::tie option by NathanOliver is significantly less error-prone than the above (although you can still easily mess up any copy-pasted stuff - I've done it before).
In C++20 there will be an even simpler solution (see cppreference):
class Person {
// ...
public:
auto operator<=>(const Person &) const = default;
}
This will basically provide all comparisons exactly as if you manually implemented them via comparisons on all members std::tied together.

Checking if Container has Value (c++)

I have a custom class 'team' and one of its attributes is its 'name.' After each 'team' is created, I add it to a vector teamList.
I would like to implement a function that continuously prompts the user for a team name which is not already taken by a team within the teamList. I have the following code:
while (true) {
string newString;
bool flag = true;
getline(cin, newString);
for (int i = 0; i < teamList.size(); i++) {
if (teamList[i].name.compare(newString) == 0) flag = false;
}
if (flag == true) {
return newString;
} else {
cout << "name already taken." << endl;
}
}
However, this code is really ugly; is there a better way to check? Also, a more general question- faced with an issue of ugly code (like this one), what kinds of steps can I take to find a new, cleaner implementation? Thanks.
I would use std::set, which deals with duplicates for you. As an example, you can see that the class is sorted by the string member, and when three are inserted in main, only two stay because two of the insertions have the same string, so they are treated equal.
#include <iostream>
#include <set>
#include <string>
struct SetThing {
SetThing(int value, const std::string &value2) : i(value), s(value2){}
int i;
std::string s;
bool operator<(const SetThing &other) const {
return s < other.s;
}
};
int main() {
std::set<SetThing> s;
s.insert(SetThing(5, "abc"));
s.insert(SetThing(4, "def"));
s.insert(SetThing(6, "abc"));
std::cout << s.size();
}
Now for inserting, you can just reprompt while the second member of the returned pair is false:
do {
//get input
} while (!teamList.insert(somethingBasedOnInput).second);
define an equality operator in team that can compare a team to a string:
bool team::operator==(string s) const
{
return(s==name);
}
Then you can use find:
vector<team>::const_iterator itr = find(teamList.begin(), teamList.end(),
newString);
if(itr!=league.end())
cout << "name already taken" << endl;

I/O overloading and reading from text files

I need to define a read and a print function for a class that has an array of objects as a private variable. I have to read in objects from a text file and print them to the screen. To do this I need to overload the << and >> operators. I understand I need to use loops to read and print the information stored in the array but I'm not sure how to accomplish this. My lecturer has given us a skeleton code which is basically function prototypes and the main function which I need to stick to. I understand how this works with public structs as I have done this exact scenario using that but the private variables of class' are tripping me up.
class EmployeeList {
public:
//Constructors
EmployeeList();
EmployeeList(istream&);
//Accessors
bool isEmpty() const;
bool isFull() const;
int size() const; //Number of employees in list
Employee item(int i) const; //i'th employee
//Mutators
void setItem(int i,const Employee& e);
//I/O functions, sets the i'th emplyee to e
void read(istream&);
void print(ostream&) const;
private:
enum {MAXSIZE = 100};
Employee list[MAXSIZE];
int count; //Number of employees in the current list
};
EmployeeList::EmployeeList() {
count = 0;
}
EmployeeList::EmployeeList(istream& in) {
//list[MAXSIZE] = in;
}
bool EmployeeList::isEmpty() const {
return (count == 0);
}
bool EmployeeList::isFull() const {
return (count == MAXSIZE);
}
int EmployeeList::size() const {
return count;
}
Employee EmployeeList::item(int i) const {
}
void EmployeeList::setItem(int i, const Employee& e) {
}
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
}
}
void EmployeeList::print(ostream& out) const {
for (int i=0; i < size(); i++) {
}
cout << out;
}
The above part is the Class EmployeeList while the below part are overloading functions. The commented out parts are ideas that I thought might work but didn't.
istream& operator>>(istream& in, EmployeeList& l) {
l.read(in);
return in;
}
ostream& operator<<(ostream& out, const EmployeeList& l) {
l.print(out);
return out;
}
Below is the main function given to us.
int main() {
authorInfo();
ifstream infile("a1in.txt");
if(!infile) {
cout << "file 'alin.txt' not found.";
return EXIT_FAILURE;
}
EmployeeList theList(infile);
cout << endl;
cout << theList.size() << " employees read:\n" << theList << endl;
process(theList);
return EXIT_SUCCESS;
}
Hope someone can steer me in the right direction! Let me know if you need more of the code. Thanks!
EDIT:
Employee read and print functions:
void Employee::read(istream& in) {
in >> name >> id >> salary;
}
void Employee::print(ostream& out) const {
out << getName() <<" "<< getID() <<" "<< getSalary() << endl;
}
Employee overloading:
istream& operator>>(istream& in, Employee& e) {
e.read(in);
return in;
}
ostream& operator<<(ostream& out, const Employee& e) {
e.print(out);
return out;
}
EDIT 2: Updated read() function. The line with the while is where the error is.
void EmployeeList::read(istream& in) {
Employee inEmployee;
while (in >> inEmployee && count < MAXSIZE) {
list[count] = inEmployee;
count++;
}
}
EDIT 3: Here is the print() function I have so far. It does indeed print but I get the default constructor information rather than information from the file. Is this a read or print function issue? I'm thinking read function still.
void EmployeeList::print(ostream& out) const {
cout << endl;
for (int i=0; i < count; i++) {
out << list[count];
}
}
Array Bounds
In your class, you have:
Employee list[MAXSIZE];
Given this, there is an error the code you tried:
EmployeeList::EmployeeList(istream& in) {
list[MAXSIZE] = in;
}
list only has elements from list[0] to list[MAXSIZE - 1]. list[MAXSIZE] is one past the end of the array, and is invalid.
Constructors
That said, I'd strongly recommend against having a constructor that takes an istream&. It is far better to construct an empty object with the default constructor, then use its read(istream&) method (via operator <<) to load the data. In other words, rather than:
EmployeeList theList(infile);
use:
EmployeeList theList;
infile >> theList;
If you're required to have a constructor that takes an istream&, just have it call read() after initializing the object:
EmployeeList::EmployeeList(istream& in): count(0) {
read(in);
}
Note that only one constructor is called, so the initialization in EmployeeList::EmployeeList() does not happen in EmployeeList::EmployeeList(istream&). I hear the new version of C++ deals with this unnecessary repetition, but for the time being that's where we are.
Naming
Another thing: your code will be less confusing with better variable names. In this case:
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
}
}
Don't say tempList because it's not a "temporary list", it's a single Employee that has been read. Better would be:
void EmployeeList::read(istream& in) {
Employee inEmployee;
while (in >> inEmployee) {
list[count++] = inEmployee;
}
}
This looks like a homework so i'll try to just give you a hint:
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
//here you are creating a tempList so after you fill in the values in tempList
//the tempList is to become a part of Employee list[MAXSIZE];
}
}
and how do you fill in the values? You do this using your constructor and maintaining the count
EmployeeList::EmployeeList(istream& in) {
//here...
}
You could start off by figuring out how to read in input. The approach, which is likely incomplete, that I would take is this:
EmployeeList::EmployeeList(istream& in) {
count = 0;
read(in); // delegate it to avoid duplication
}
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList && count < MAXSIZE) {
list[count] = tempList;
++count;
}
}
You will need to overload operator>> for Employee class for this to work.
Here is how I would write this, without the skeleton constraint. Feel free to adapt to your assignment requirements.
Source: http://www.ideone.com/R9EeF
Iostreams are hard to master. You have to read about std::getline, the std::ios flags and stringstreams to understand how to parse an employee list with them.
I prefer giving you a working template (that you cannot use for your assignment since I don't make use of the skeleton at all), since there is a lot to say about iostreams.
Also feel free to ask questions, so that I can enhance my answer with your actual problems.