unable to find() struct attribute inside unordered_set - c++

I have the following code for add a structure to an unordered_set. Now i want to search if a name of a student is already in the unordered_set. How do i do this? When i create a key i need to pass three parameters but i want to search just for the first one, the name. If i create a key just for the first parameter i got an error.
#include <iostream>
#include <unordered_set>
using namespace std;
struct Person {
string name, biology;
int scoreBio;
//param constructor
Person(string pName, string pBiology, int pscoreBio)
{
name = pName;
biology = pBiology;
scoreBio = pscoreBio;
}
bool operator==(const Person& h) const
{
return name == h.name && biology == h.biology && scoreBio == h.scoreBio;
}
};
class MyHashFunction {
public:
// We use predfined hash functions of strings
// and define our hash function as XOR of the
// hash values.
size_t operator()(const Person& h) const
{
return (hash<string>()(h.name)) ^ (hash<string>()(h.biology)) ^ (hash<int>()(h.scoreBio));
}
};
int main()
{
unordered_set<Person, MyHashFunction> Student;
Person p1("Mauro", "Biology", 56);
Person p2("Ram", "Singh", 67);
Person p3("kartik", "kapoor", 56);
Student.insert(p1);
Student.insert(p2);
Student.insert(p3);
Person key("Mauro", " ", 0);
if (Student.find(key) == Student.end())
cout << " not found" << endl << endl;
else
cout << "Found " << endl << endl;
for (auto e : Student) {
cout << e.name << " " << e.biology << " " << e.scoreBio << endl;
}
return 0;
}

The find function in unordered_set searches for keys in the set. It looks for a match against a Person, since that's the type of the key in the map. There are no Persons in the map with a value of { "Mauro", " ", 0 }, so the find call is returning end().
There's no provision in the member find call for searching for part of the key.
You could use the free algorithm find with a custom predicate here:
std::find(Student.begin(), Student.end(),
[] (const Person &p) { return p.name == "Mauro"; });
but that will perform a linear search of the collection, not a hash-based lookup.

Related

priority queue using class object

I am trying to make a priority queue with Student class.
I overloaded operator< function which compare student id of two objects of Student class, but my code does not work.
Could you give me any advice?
#include <iostream>
#include <queue>
#include <vector>
#include <string>
using namespace std;
class Student {
public:
string name;
int id;
Student(string str, int n) {
this->name = str;
this->id = n;
};
};
bool operator<(const Student& a, const Student& b) {
return a.id < b.id;
}
int main() {
priority_queue<Student> pq;
pq.push(Student("Miria", 1));
pq.push(Student("Ken", 2));
pq.push(Student("Bob", 3));
while (!pq.empty()) {
cout << pq.top() << endl;
pq.pop();
}
return 0;
}
I am kind of unaware what an "My code does not work seems", although it seems to me that you are not printing the objects in the priority_queue correctly.
The appropriate way of printing them will be by using the object's properties or using a string representation operator overload. Let me give you an example of how the simpler version of the code would look like by using your code:
cout << pq.top().name << "'s id is: " << pq.top().id << endl;
The reason your code is not working is because you are lacking the output operator overload. You are not able to compile the program itself if you are lacking this operator.
The former spoken of is done the foreshown code down below:
friend ostream& operator<<(std::ostream& output, const Student& obj) {
return output << obj.name << "'s id is: " << obj.id << endl;
};
This way you will be able to use the pq.top() function in the cout stream:
cout << pq.top();
I hope this is what you expect and if you want to make it in increase order like Java you can use priority_queue.
The expected output as the question is described should be:
Bob's id is: 3
Ken's id is: 2
Miria's id is: 1

A vector holds class objects, The class object contains 3 strings per object. How do i find the specific string, then delete the entire element?

I have a class that contains 3 elements for example {first_name, Last_name, Phone}
I have a vector that holds this set of information. In what manner could I go about looking for a single element of the set, for example find(last_name), and delete all elements that contain that specific last name?
I've tried many examples and have searched far and wide throughout the world wide google. Please help. Attached is bits of code:
int number = 4;
vector <Friend> BlackBook(number);
Friend a("John", "Nash", "4155555555");
Friend d("Homer", "Simpson", "2064375555");
BlackBook[0] = a;
BlackBook[1] = d;
Now that's just same basic code for the set up. Here's a couple of things i've tried. But the more I look at what the code says, the more it seems as if it's not allowing for a string argument... but then i don't know how to give a class arguement with respect to a specific string... well I don't know what i'm doing wrong. I have a feeling I could do this with pointers, but the whole pointer thing isn't clicking yet. But heres some things i've tried.
vector <Friend> :: iterator frienddlt;
frienddlt = find (BlackBook.begin(), BlackBook.end(), nofriend);
if (frienddlt != BlackBook.end())
{
BlackBook.erase( std::remove( BlackBook.begin(), BlackBook.end(), nofriend), BlackBook.end() );
}
else
{
cout << nofriend <<" was not found\n" << "Please Reenter Last Name:\t\t";
}
When I compile the project the header file stl_algo.h opens and points to line 1133.
Any Help would be much appreciated!! thank you!
Try remove_if
My example:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
struct Friend {
string first_name;
string last_name;
string phone;
};
bool RemoveByName (vector<Friend>& black_book, const string& name) {
vector<Friend>::iterator removed_it = remove_if(
black_book.begin(), black_book.end(),
[&name](const Friend& f){return f.first_name == name;});
if (removed_it == black_book.end())
return false;
black_book.erase(removed_it, black_book.end());
return true;
}
int main() {
vector <Friend> black_book {
Friend {"John", "Nash", "4155555555"},
Friend {"Homer", "Simpson", "2064375555"}
};
if (RemoveByName(black_book, "John")) {
cout << "removed" << endl;
} else {
cout << "not found" << endl;
}
if (RemoveByName(black_book, "Tom")) {
cout << "removed" << endl;
} else {
cout << "not found" << endl;
}
for (int i = 0; i < black_book.size(); ++i) {
Friend& f = black_book.at(i);
cout << f.first_name << " " << f.last_name << " " << f.phone << endl;
}
return 0;
}
Output:
removed
not found
Homer Simpson 2064375555
Of course, you can always loop over all Friend elements and delete them manually.
Blackbook::iterator friend = Blackbook.begin();
while (friend != Blackbook.end())
{
if (friend->last_name == bad_name)
{
friend = Blackbook.erase(friend);
}
else
{
++friend;
}
}

Error in trying to find something in a Vector, Unsure how to fix

So I have a .txt file that I'm pushing into a vector, and that's worked all right.
I'm trying to take user input (string), and find a match in the vector. This i my code so far:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <list>
using namespace std;
struct customer{
string name;
float disc;
};
class customer_list {
vector<customer> custlist;
public:
void load(string fname){
ifstream list;
list.open ("customers.txt");
customer c;
double val = 0;
if (list.is_open()){
while (! list.eof() ){
list >> c.name;
custlist.push_back(c);
list >> c.disc;
custlist.push_back(c);
val++;
}
}
list.close();
int val2 = custlist.size()/val;
for (int j=0; j < custlist.size() - 2; j+= val2){
cout << " Name: " << custlist[j].name << endl;
cout << " Discount: " << custlist[j+1].disc << endl;
}
}
bool in_list(string & query){ //return true if query in list, false otherwise
if (find (custlist.begin(), custlist.end(), query) !=custlist.end()){
cout << "Welcome back," << query << "." <<endl;
return true;
}else{
cout << "No Matches found." << endl;
return false;
}
}
};
int main (){
customer_list a;
string name;
a.load("customers.txt");
cout << "What is your name?" << endl;
cin >> name;
a.in_list(name);
}
I get this error when run:
In file included from prt2.cpp:1:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iostream:38:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ios:216:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__locale:15:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:439:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:865:22: error: invalid operands
to binary expression ('customer' and 'const std::__1::basic_string<char>')
if (*__first == __value_)
~~~~~~~~ ^ ~~~~~~~~
prt2.cpp:46:15: note: in instantiation of function template specialization 'std::__1::find<std::__1::__wrap_iter<customer *>,
std::__1::basic_string<char> >' requested here
if (find (custlist.begin(), custlist.end(), query) !=custlist.end()){
^
1 error generated.
Any help appreciated! Thanks.
Your find is comparing apples to oranges. The search value sought with std::find must be of the same or convertible to, the type of the sequence, That isn't strictly true, but usually is practiced. You can circumvent that with an equivalence operator that compares the two values (operator ==), even if they're different types, but that madness really isn't needed. Regardless, you're searching for a std::string in a customer-filled jungle.
You can step around that by using other search methods, such as std::find_if and a custom qualifying lamda expression:
Like this:
bool in_list(const string & s)
{
if (std::find_if(custlist.begin(), custlist.end(),
[&s](const customer& c) { return c.name == s; }) != custlist.end())
{
cout << "Welcome back," << s << "." <<endl;
return true;
}
cout << "No Matches found." << endl;
return false;
}
There are other ways, of course.
Anyway, best of luck.
You need to include : #include <algorithm>
to use std::find correctly.
And it will works.
You can also improuve this part of your code :
if (list.is_open()){
while (! list.eof() ){
list >> c.name;
custlist.push_back(c);
list >> c.disc;
custlist.push_back(c);
val++;
}}
By using this:
if (list.is_open()){
while (! list.eof() ){
list >> c.name;
list >> c.disc;
custlist.push_back(c);
val++;
}}
because you use 2 elem of your vector to store only one elem, example if you have yop 42.42, you will have in your vector :
vector[0].name = "yop";
vector[0].disc = undifined_value;
vector[1].name = "";
vector[1].disc = 42.42;
After the correction you will have:
vector[0].name = "yop";
vector[0].disc = 42.42;
To use the function std::find on a structure you have to add also this function :
bool operator==(const customer &c, const string &name){
return (c.name == name);
}
std::find will call this function to check the values.
You simply need to use operator overloading, for you are using vectors with customized type, however the method std::find() in vector can only compare value in primitive types. Try the following function:
bool operator ==(const customer_list& other) const{
return other.name==this->name;
}

C++ List Iterators accessing different elements in the same object

for List Iterators i can access different elements
but i can only access one thing per object
eg
class student{
private :
string firstname;
string lastname;
// some other variables //etc...
public:
// some functions here ....
};
in the list i can access and print all the first name with the List Iterators
but how do I print
first name follow by last name in a List ?
This is what I think you're talking about: if not, please give some more details.
I take it you've got a std::list of student, like
std::list<student> studentList;
//add student instances to list
And then you're iterating over it, something like so:
for(std::list<student>::it = studentList.begin(); it != studentList.end(); ++it)
{
std::cout << it->getFirstName() << std::endl;
}
If this is the case, just add a bit more for it->getLastName():
for(std::list<student>::it = studentList.begin(); it != studentList.end(); ++it)
{
std::cout << it->getFirstName() << " " << it->getSecondName() << std::endl;
}
You can print lastname in the same way, you print the firstname. If you print it in a member function of the class, then you can do this:
std::cout <<firstname <<" " <<lastname << std::endl;
And if you've written some get functions , and you print it from non-member function of the class, then you can do this:
student s;
//...
std::cout <<s.getFirstname() <<" " <<s.getLastname() << std::endl;
You can also add operator<< friend function to the class, as:
class student{
private :
string firstname;
string lastname;
// some other variables //etc...
public:
// some functions here ....
friend std::ostream& operator<<(std::ostream & out, const student &s)
{
return out << s.firstname <<" " <<s.lastname;
}
};
then do this:
std::list<student> students;
//...
for(std::list<student>::iterator it = students.begin(); it != students.end(); it++ )
{
std::cout << *it << std::endl;
}
And you can do even this:
student s;
//...
std::cout << s << std::endl; //it prints firstname and lastname by calling operator<<
Simple ! cout the firstname it followed by the last name.
cout << lIter->firstName << " " << lIter->lastName << endl ;
// firstName, lastName are private. So, intead write getters and call those on list iterator.
Or did I understand the question wrong ?

Storing a objects derived from an abstract base class with maps in a vector array of base class pointers

I'm writing a program that uses OOP to store student records. At the moment I only have two classes, one for each individual course module called 'Courses', and one ( well two if you count the abstract base class) for the type of degree programme called 'Physics' derived from the 'Records' base class.
I'm using two maps in the program. One to store the individual courses for each individual record and sort them by course code, and one to store all the records and sort them by ID numbers.
I planned on having the user input all student information, including codes, storing this in a vector (named 'prec' in the code), then pushing the vector elements into the map used to store all the records. The code is far from finished, I was just attempting to run it to see if I was on the right track.
The code builds without any errors, but when I attempt to run it, it comes up with the error message: " Debug assertion failed: expression vector subscript out of range". I feel this may have something to do with the way I am using individual vector elements to call my functions to store courses in the maps but I cant quite get it, any help would be much appreciated!
Here are my files:
header file:
#ifndef MY_CLASS_H // Pre-processor directives to prevent multiple definition
#define MY_CLASS_h
#include <iostream>
#include <string>
#include <utility>
#include <map>
#include <fstream>
using std::string;
using std::ostream;
using std::map;
using std::cout;
using std::endl;
using std::cin;
namespace student_record // Defines the namespace student_record in which the classes are defined
{
class Course { /* Create class Course for individual courses, is this better than incorporating
all the data separately into the Record class below? Class contains course name, mark achieved and mark weight and course ID */
protected:
string course_name;
double course_mark;
int course_Id;
public:
Course() {course_name= "Null"; // Default constructor for null course
course_mark=0;
}
Course(string course_namein, double course_markin, int course_Idin) {course_name=course_namein; // Parametrized constructor to create course with set name, mark, weight and course ID
course_mark=course_markin;
course_Id=course_Idin;}
~Course() {course_name.erase(0,course_name.size());} // Destructor to delete the course name
// Access functions to get name, mark and weight //
double getmark() const {return course_mark;}
string getname() const {return course_name;}
int getid() const {return course_Id;}
friend ostream & operator << (ostream &os, const Course &c); // Friend function to overload the insertion operator for courses
};
class Record
{ // Create class Record as abstract base class for all inherited degree classes
protected:
string student_name;
int studentid;
int years;
public:
Record() {student_name="Casper";
studentid=0;
years=0;} // Default constructor for class Record, produces empty record
Record(string name, int number, int time) {student_name=name;
studentid=number;
years=time;} // Parametrized constructor for class Record
~Record() {student_name.erase(0, student_name.size());} // Destructor to delete the student name
virtual int getid()const=0;
virtual int getyears()const=0;
virtual void show_record()const=0;
virtual void print_record(string *filename)const=0;
virtual void degree_class()const=0;
virtual void insert_class()=0;
/* Virtual functions defined to be used in the derived classes (subjects ie, Physics, stamp collecting, etc...)
Thus the base class Record is abstract*/
};
class Physics: public Record
{
private:
string degree_name;
typedef map <int, Course> course_map;
course_map modules;
void searchdatabase (course_map &courses, int coursecode)const; // Uses iterator to search map for corresponding course to inputted key ( remember to move to function definitions)
string get_name (const int i, course_map &temp) const{ return temp[i].getname();}
double get_mark(const int i, course_map &temp)const{ return temp[i].getmark();} // Functions to return the mark, weight and name of a given course corresponding to inputed course code
int getid()const{return studentid;}
int getyears()const{return years;}
void show_record()const;
void print_record( string *filename) const;
void degree_class()const;
void insert_class();
// Function to insert record into map
public:
Physics():Record(){degree_name= "Physics ";}
Physics(string name,int Id, int time):Record( name, Id, time){degree_name= "Physics";}
~Physics() {degree_name.erase(0, degree_name.size());}
};
}
#endif
function definitions:
#include <iostream>
#include <string>
#include <utility>
#include <map>
#include <fstream>
#include <vector>
#include "Database_header.h"
using namespace std;
using namespace student_record;
ostream & student_record::operator<< (ostream &os, const Course &c)
{
os<< "Course code" << c.course_Id << " \n Course name: " <<c.course_name << " \n Mark " << c.course_mark <<endl;
return os;
}
// Function to insert classes //
void Physics::insert_class()
{
int courseid;
string coursename;
double mark;
cout << " Enter course code " << endl;
cin >> courseid;
cout << " \n Enter course name " << endl;
cin >> coursename;
cout << " \n Enter mark achieved " << endl;
cin >> mark;
Course temp (coursename, mark, courseid);
modules.insert(pair<int, Course>(courseid, temp));
}
void Physics::searchdatabase(course_map &courses, int coursecode) const // Function to search for specific course mark based on course code, need to modify this!!!!
//takes in a map as its argument, although i suppose can use student.modules?
{
course_map::iterator coursesIter;
coursesIter=courses.find(coursecode);
if(coursesIter != courses.end())
{
cout << " Course Code " <<
coursecode << " corresponds to " <<
coursesIter ->second << endl;
}
else { cout << " Sorry, course not found " << endl; }
}
void Physics::print_record( string *filename) const // Function for printing record to the file
{
ofstream myoutputfile;
myoutputfile.open(*filename,ios::app);
if(!myoutputfile.good())
{
// Print error message and exit
cerr<<"Error: file could not be opened"<<endl;
}
if(myoutputfile.good())
{
myoutputfile << "Student name: " << student_name << endl
<< "\n Student ID: " << studentid << endl
<< "\n Year: " << years << endl;
course_map::iterator modulesiter; // Iterator to print out courses using overloaded << function (I think?)
for(modulesiter==modules.begin();modulesiter!=modules.end();modulesiter++)
{
myoutputfile<<modulesiter->second << endl;
}
}
}
void Physics::show_record() const // Function for showing specific student record on screen ( with iterator for map of courses)
{
cout << "Student name: " << student_name;
cout << "\n Student ID: " << studentid;
cout << "\n Years on course: " << years;
cout << "\n Courses and grades: ";
course_map::iterator modulesiter; // Iterator to print out courses using overloaded << function (I think?)
for(modulesiter==modules.begin();modulesiter!=modules.end();modulesiter++)
{
cout<<modulesiter->second << endl;
}
}
void Physics::degree_class()const
{
double temp;
vector<double> dynarr; // Create a vector array to store the grades extracted from the course map for each student
course_map::iterator modulesiter;
for(modulesiter==modules.begin();modulesiter!=modules.end();modulesiter++) // Iterate through map and push values into each vector
{
Course ghost;
ghost=modulesiter->second;
dynarr.push_back(ghost.getmark());
}
double sum(0);
for(int i(0);i<=dynarr.size();i++)
{
sum+=dynarr[i];
}
temp=sum/dynarr.size();
if( temp>=40 && temp <=49.9)
{
cout << "The student has achieved a 3rd class degree with an average of: \n "
<< temp;
}
else if( temp>=50 && temp <=59.9)
{
cout << "The student has achieved a 2:2 degree with an average of: \n "
<< temp;
}
else if( temp>=60 && temp <=69.9)
{
cout << "The student has achieved a 2:1 degree with an average of: \n "
<< temp;
}
else if( temp>=70)
{
cout << "The student has achieved a 1st class degree with an average of: \n "
<< temp;
}
else { cout << "The student has failed the degree " << endl;}
}
and main cpp file:
#include <iostream>
#include <utility>
#include <map>
#include <iomanip>
#include <vector>
#include "Database_header.h"
#include <fstream>
using namespace std;
using namespace student_record;
void main()
{
// Create map to store students with ID keys //
string full_name;
int id;
int time;
string degree_name;
vector<Record*> prec;
// Vector of base class pointers to store all the different records first. No need to specify length as it is a vector! (Advantage over dynamic array?)
char student_test('y'); // Condition for adding students to the record //
int q(0);
while (student_test=='y' || student_test=='Y')
{
// Counter for while loop
cout<< " \n Please enter the student name " << endl;
getline(cin, full_name);
// Enter student name, check it is a string? //
cout<< "\n Please enter student ID " << endl;
cin >> id;
// Check if not integer or number, if not need error message //
cout << "\n Please enter the number of years on the course " << endl;
cin >> time;
// Check if not integer or number, if not need error message //
cout<< "\n Please enter degree type " << endl;
cin>>degree_name;
if(degree_name=="Physics" || degree_name=="physics") // create object of appropriate derived class ( Physics, Chem, Maths, Bio)
{
prec.push_back(new Physics(full_name, id, time));
}
char class_test('y'); // test condition for class insertion loop
while(class_test=='y') // Add courses+marks into course map
{
cout << " \n Add classes to student record " << endl;
prec[q]->insert_class();
cout << " \n Add another class? Y/N" << endl;
cin>>class_test;
}
cout << "Enter another student? Y/N " << endl;
cin >> student_test;
if(student_test=='N' && student_test=='n')
{
cout << "\n Thank you for using the student database, Goodbye !" << endl;
}
q++; // increment counter, to keep track of of vectors of base class pointers, and also be able to output number of students
}
// Next insert all records into map //
typedef map<int, Record*> studentlist;
studentlist studentmap;
for(int i(0); i<=prec.size(); i++)
{
studentmap.insert(pair<int, Record*> (prec[i]->getid(), prec[i]));
}
}
Thanks so much!
for(int i(0); i<=prec.size(); i++)
{
studentmap.insert(pair<int, Record*> (prec[i]->getid(), prec[i]));
}
Should be i < prec.size() instead of <=