I've been trying to sort a vector of Employee's with a string data member called last name. I've tried several different ways, using the sort method of vector, trying to convert my vectors to list and using its sorting, I even tried using string compare and > operators as shown below:
vector<Employee>sortE(vector<Employee>record)
{
for (unsigned int i = 0; i < record.size() - 1; i++)
if (record[i].getLastName() > record[i+1].getLastName())
swap(record[i], record[i + 1]);
return record;
}
I thought if I used the above method with the swap function, it would work. But maybe since swap is a string method and I'm doing it with Employees it won't swap properly? But I've also tried it with my own "swap" like below:
vector<Employee>sortE(vector<Employee>record)
{
Employee temp;
for (unsigned int i = 0; i < record.size() - 1; i++)
if (record[i].getLastName() > record[i + 1].getLastName())
{
temp = record[i];
record[i] = record[i + 1];
record[i + 1] = temp;
}
return record;
}
Either way I can't seem to get it to work properly, any insight or help would be appreciated.
You could try using a lambda if using C++11 or newer (also, I don't know what your Employee class looks like, so I made a trivial one). Also, check here for online execution: http://cpp.sh/6574i
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
class Employee
{
public:
Employee( const std::string& firstName, const std::string& lastName ) :
_firstName( firstName ),
_lastName( lastName )
{}
~Employee()
{}
std::string FirstName() const
{
return _firstName;
}
std::string LastName() const
{
return _lastName;
}
std::string FullName() const
{
return _firstName + " " + _lastName;
}
private:
std::string _firstName;
std::string _lastName;
};
int main()
{
Employee e1( "Suresh", "Joshi" );
Employee e2( "Mats", "Sundin" );
Employee e3( "Steve", "Nash" );
std::vector< Employee > employees { e1, e2, e3 };
std::sort(employees.begin(), employees.end(),
[](const Employee& lhs, const Employee& rhs) -> bool
{
return rhs.LastName() > lhs.LastName();
});
for ( auto employee : employees )
{
std::cout << employee.FullName() << std::endl;
}
}
You can provide a lambda to std::sort:
std::vector<Employee> ve;
using std::begin;
using std::end;
std::sort(begin(ve), end(ve),
[](const Employee& lhs, const Employee& rhs)
{
return lhs.getLastName() < rhs.getLastName();
});
That said, in real life last names are not necessarily unique, and when they compare equal it's a good idea to fall back on first name, and if that's also equal some other field like an employee id:
return lhs.getLastName() < rhs.getLastName() ||
lhs.getLastName() == rhs.getLastName() &&
(lhs.getFirstName() < rhs.getFirstName() ||
lhs.getFirstName() == rhs.getFirstName() &&
lhs.getId() == rhs.getId());
Related
Hey i have a table of teams with the names and the points they have and i'm trying to figure out how to display the last 3 teams with the least amount of points in the table?
It displays all the teams and i want it to display only the last 3 in the table but don't know what way to go about it.
These are my Accessors
string GetName
int GetPoints
int lowest = 1000;
for (int i = 0; i < numTeams; i++)
{
if (league[i].GetPoints() < lowest)
{
lowest = league[i].GetPoints();
}
}
for (int i = 0; i < numTeams; i++)
{
if (league[i].GetPoints() == lowest)
{
cout << "\tThe lowest goals against is: " << league[i].GetName() << endl;
}
}
Actually, you don't need variable lowest, if you would sort the data before printing.
#include <algorithm>
// Sort using a Lambda expression.
std::sort(std::begin(league), std::end(league), [](const League &a, const League &b) {
return a.GetPoints() < b.GetPoints();
});
int last = 3;
for (int i = 0; i < last; i++)
{
cout << "\tThe lowest goals against is: " << league[i].GetName() << endl;
}
U could probably start by sorting your array
#include <algorithm>
std::array<int> foo;
std::sort(foo.begin(), foo.end());
and then Iterate From Your Last Element to your Last - 3. (U can use Reverse Iterators)
for (std::vector<int>::reverse_iterator it = v.rend() ; it != v.rend() + 3;
it++) {
//Do something
}
or by using auto
for (auto it = v.rend() ; it != v.rend() + 3; ++it) {
//Do something
}
In my example I've created test class(TestTeam) to implement several important methods for objects in your task.
I use std::sort method to sort container of objects, by default std::sort compares objects by less(<) operation, so I have overrided operator < for TestTeam object
bool operator < ( const TestTeam& r) const
{
return GetPoints() < r.GetPoints();
}
Also we could pass as third parameter another compare method or lambda method as shown in below answers:
std::sort(VecTeam.begin(), VecTeam.end(), [](const TestTeam& l, const TestTeam& r)
{
return l.GetPoints() < r.GetPoints();
});
And example when we use global method to compare:
bool CompareTestTeamLess(const TestTeam& l, const TestTeam& r)
{
return l.GetPoints() < r.GetPoints();
}
//...
// some code
//...
// In main() we use global method to sort
std::sort(VecTeam.begin(), VecTeam.end(), ::CompareTestTeamLess);
You can try my code with vector as container:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
// Test class for example
class TestTeam
{
public:
TestTeam(int16_t p, const std::string& name = "Empty name"):mPoints(p), mName(name)
{
};
int16_t GetPoints() const {return mPoints;}
const std::string& GetName() const {return mName;}
void SetName( const std::string& name ) {mName=name;}
bool operator < ( const TestTeam& r) const
{
return GetPoints() < r.GetPoints();
}
private:
int16_t mPoints;
std::string mName;
};
int main(int argc, const char * argv[])
{
const uint32_t COUNT_LOWEST_ELEMENTS_TO_FIND = 3;
// Fill container by test data with a help of non-explicit constructor to solve your task
std::vector<TestTeam> VecTeam {3,5,8,9,11,2,14,7};
// Here you can do others manipulations with team data ...
//Sort vector by GetPoints overloaded in less operator. After sort first three elements will be with lowest points in container
std::sort(VecTeam.begin(), VecTeam.end());
//Print results as points - name
std::for_each( VecTeam.begin(), VecTeam.begin() + COUNT_LOWEST_ELEMENTS_TO_FIND, [] (TestTeam el)
{
std::cout << el.GetPoints() << " - " << el.GetName() << std::endl;
} );
}
I made test class TestTeam only to implement test logic for your object.
If you try launch the program you can get next results:
2 - Empty name
3 - Empty name
5 - Empty name
Program ended with exit code: 0
I am trying to run binary_search on vector of custom objects.
struct T{
string name;
T(string n):name(n){};
bool operator < ( T * n ) const {
return name < n -> name;
}
bool operator == ( T * n ) const {
return name == n -> name;
}
};
vector<T *> t;
t.push_back(new T("one"));
t.push_back(new T("two"));
t.push_back(new T("three"));
bool has_3 = binary_search( t.begin(), t.end(), new T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
The comparation function should be just fine yet when i run the code has_3 equals to 0 = the element isnt present in vector. Is this problem caused by my overloading of < ? I see no reason why this shouldnt find the value. Considering the order of insertion into vector it should be sorted
Thanks for help.
There are several reasons why this shouldn't find the value:
The range must be sorted; your range is out of alphabetical order
Your comparison functionality is defined between T and T*, while you search a vector of T* for a T*.
You can fix the first problem by swapping "two" and "three", and the second problem by making a vector of T:
struct T{
string name;
T(string n):name(n){};
bool operator < ( const T &n ) const {
return name < n.name;
}
// operator == is not necessary for binary_search
};
int main() {
vector<T> t;
t.push_back(T("one"));
t.push_back(T("three"));
t.push_back(T("two"));
bool has_3 = binary_search( t.begin(), t.end(), T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
return 0;
}
Demo 1.
If you do have no way but to construct a vector of pointers, you have this ugly work-around available (I strongly recommend against it):
struct T{
string name;
T(string n):name(n){};
};
bool operator < (const T& l, const T *r) {
return l.name < r->name;
}
bool operator < (const T *l, const T &r) {
return l->name < r.name;
}
Now you can search like this:
bool has_3 = binary_search( t.begin(), t.end(), T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
Demo 2.
It's a really dumb requirement to work with a vector of pointers to dynamically allocated objects. But here is an approach that will work.
#include <iostream>
#include <string>
#include <algorithm>
struct T
{
std::string name;
T(std::string n):name(n){};
};
// this is the comparater needed to work with pointers, but it should
// NOT be a member of T
bool pointer_comparer(const T *left, const T *right)
{
// this assumes both left and right point to valid objects
return left->name < right->name;
}
int main()
{
std::vector<T *> t;
t.push_back(new T("one"));
t.push_back(new T("two"));
t.push_back(new T("three"));
// t is unsorted. We need to sort it since binary_search will
// ASSUME it is sorted
std::sort(t.begin(), t.end(), pointer_comparer);
T *value_needed = new T("two");
bool has_3 = std::binary_search( t.begin(), t.end(), value_needed, pointer_comparer);
if(has_3)
{
std::cout <<"Its there" << std::endl;
}
// since we've been stupidly allocating objects, we need to release them
delete value_needed;
for (std::vector<T *>::iterator i = t.begin(), end = t.end();
i != end; ++i)
{
delete (*i);
}
// and since t now contains a set of dangling pointers, we need to discard them too
t.resize(0);
return 0;
}
Why do I say the requirement to work with a vector of pointers to dynamically allocated objects. Compare the above with an approach that works with a vector<T> rather than a vector<T *>.
#include <iostream>
#include <string>
#include <algorithm>
struct T
{
std::string name;
T(std::string n):name(n){};
bool operator < (const T &) const
{
return name < n.name;
};
};
int main()
{
std::vector<T> t;
t.push_back(T("one"));
t.push_back(T("two"));
t.push_back(T("three"));
// t is unsorted. We need to sort it since binary_search will
// ASSUME it is sorted
std::sort(t.begin(), t.end());
bool has_3 = std::binary_search(t.begin(), t.end(), T("two"));
if(has_3)
{
std::cout <<"Its there" << std::endl;
}
// we need do nothing here. All objects use above will be properly released
return 0;
}
Note: I've written the above so it works with ALL C++ standards. Assuming C++11 and later, simplifications are possible in both cases.
I have class:
class MyClass {
public:
void SetName(std::string name) { Name = name; }
void SetAge(int age) { Age = age; }
void SetId(int id) { Id = id; }
void SetNationality(std::string nationality) { Nationality = nationality; }
//.. other set functions
std::string GetName() { return Name; }
int GetAge() { return Age; }
int GetId { return Id; }
//.... other Get functions
Private:
std::string Name;
int Age;
int Id;
std::string Nationality;
//... (many other variables)
};
Then I have one function where I make and fill vector (std::vector<MyClass> MyVector)
This function isn't very important so I didn't write it here.
Then I have function where I use my vector:
void MyFun(std::vector<MyClass> vec)
{
// Now I need to print vector elements Age and Name
for (std::vector<MyClass>::iterator it = vec.begin(); it != vec.end(); it++) {
// but if vector has two or more elements which have same Name and Age,
// I print only the element which has the biggest Id and other elements I
// erase from vector
// if (...) {}
std::cout << it->GetName << " :Name; Age: " << it->GetAge << std::endl;
}
}
Can anybody help me with that?
Important is that, if one of element's parameter(Age or Name), is different, then I print both vector elements name and age. Others variable values don't matter. They can be different or same.
To remove duplicates, you can first sort the vector with std::sort and then use std::unique to remove the duplicates. If the duplicates are already in consecutive elements, you can skip std::sort and just use std::unique.
To make these work, you need to tell them how to compare the elements.
bool less_name_age( MyClass const &lhs, MyClass const &rhs ) {
return lhs.name < rhs.name? true // ascending alphabetical names
: rhs.name < lhs.name? false
: lhs.age < rhs.age? true // ascending ages
: rhs.age < lhs.age? false
: rhs.id < lhs.id; // descending order of ID
}
bool equal_name_age( MyClass const &lhs, MyClass const &rhs ) {
return lhs.name == rhs.name && lhs.age == rhs.age;
}
std::sort( vec.begin(), vec.end(), less_name_age );
std::vector< MyClass >::iterator new_end
= std::unique( vec.begin(), vec.end(), equal_name_age );
I omitted the getter/setter idiom because life is too short.
I have problem with set. I don't know what I'm doing wrong. Maybe some one of you can help me. So lets begin , the output of my program should be :
Iksinski Adam, Kowalski Jan, Nowak Adam, Nowak Jan,
So its sorted by first string.
And here's my program :
#include <set>
#include <iterator>
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;
class Person{
public:
Person(){}
Person(string v , string v1):nazw(v),imie(v1){}
bool operator<(const Person & K) const
{
return ((this->getN()>K.getN())?0:1);
//return ((this->getN()<K.getN())?1:0);
}
string getN()const
{
return nazw;
}
/*
bool operator()(Person & K, Person & K1)
{
return ((K->getN()<K1.getN())?1:0);
}
*/
friend ostream& operator<<(ostream & o , const Person&K)
{
o << K.nazw << " " << K.imie;
return o;
}
private:
string nazw,imie;
};
struct cmp
{
bool operator()(const Person &K , const Person &K1)
{
return ((K.getN()<K.getN())?1:0);
}
};
int main()
{
//typedef set<Person> kontener_typ;
typedef set<Person,cmp> kontener_typ;
kontener_typ c;
c.insert(Person("Nowak","Jan"));
c.insert(Person("Nowak","Adam"));
c.insert(Person("Kowalski","Jan"));
c.insert(Person("Nowak","Adam"));
c.insert(Person("Iksinski","Adam"));
std::copy (c.begin(), c.end(), ostream_iterator<Person>(cout, " ,"));
std::cout << std::endl;
}
Ok so in main i can only edit typdef , and copy function ( but I need to use it to output the set).
Like you see i tried to overload operator< in Person (because set compering Person to Person) but it doeasnt work . I also trying with functor but it then the output looks like
Iksinski Adam ,Nowak Adam ,Kowalski Jan ,Nowak Adam ,Nowak Jan ,
So second string should be deleted .
Good luck :).
Your code is using your comparator cmp functor object. There is a bug in it:
struct cmp
{
bool operator()(const Person &K , const Person &K1)
{
// one of these _should_ be K1
return ((K.getN()<K.getN())?1:0);
}
};
I like to name my variables in a way that it becomes clear how they are being compared, for instance:
struct cmp
{
bool operator()(const Person &left , const Person &right)
{
return left.getN() < right.getN();
}
};
This clarifies (for me at least) that the operator is comparing like this: left < right.
However, you also need to sort by "first name" as a secondary criteria, which would make the function look like this:
struct cmp
{
bool operator()(const Person &left , const Person &right)
{
if(left.getN() < right.getN())
return true;
else if(left.getN() > right.getN())
return false;
// assuming the getI() function returns the first name,
// just as the getN() function returns the last name
else if(left.getI() < right.getI())
return true;
else
return false;
}
};
You must compare by both last and first names:
bool operator<(const Person & other) const
{
if ( getN() < other.getN() )
return true;
if ( other.getN() < getN() )
return false;
if ( imie < other.imie() )
return true;
return false;
}
Or, if you want to use struct cmp:
struct cmp
{
bool operator()(const Person &K , const Person &K1)
{
if (K.getN() < K1.getN())
return true;
if(K1.getN() < K.getN())
return false;
if(K.imie < K1.imie) // will need a friend declaration or a getter() func
return true;
return false;
}
}
If you have C++'s std::tie, then the guts of either function gets much simpler:
return std::tie(nazw, imie) < std::tie(other.nazw, other.imie);
return std::tie(K.nazw, K.imie) < std::tie(K1.nazw, K1.imie);
I was given the following forumulae for calculating this
sim=|Q∩D| / √|Q|√|D|
I went ahed and implemented a class to compare strings consisting of a series of words
#pragma once
#include <vector>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
class StringSet
{
public:
StringSet(void);
StringSet( const string the_strings[], const int no_of_strings);
~StringSet(void);
StringSet( const vector<string> the_strings);
void add_string( const string the_string);
bool remove_string( const string the_string);
void clear_set(void);
int no_of_strings(void) const;
friend ostream& operator <<(ostream& outs, StringSet& the_strings);
friend StringSet operator *(const StringSet& first, const StringSet& second);
friend StringSet operator +(const StringSet& first, const StringSet& second);
double binary_coefficient( const StringSet& the_second_set);
private:
vector<string> set;
};
#include "StdAfx.h"
#include "StringSet.h"
#include <iterator>
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <cmath>
StringSet::StringSet(void)
{
}
StringSet::~StringSet(void)
{
}
StringSet::StringSet( const vector<string> the_strings)
{
set = the_strings;
}
StringSet::StringSet( const string the_strings[], const int no_of_strings)
{
copy( the_strings, &the_strings[no_of_strings], back_inserter(set));
}
void StringSet::add_string( const string the_string)
{
try
{
if( find( set.begin(), set.end(), the_string) == set.end())
{
set.push_back(the_string);
}
else
{
//String is already in the set.
throw domain_error("String is already in the set");
}
}
catch( domain_error e)
{
cout << e.what();
exit(1);
}
}
bool StringSet::remove_string( const string the_string)
{
//Found the occurrence of the string. return it an iterator pointing to it.
vector<string>::iterator iter;
if( ( iter = find( set.begin(), set.end(), the_string) ) != set.end())
{
set.erase(iter);
return true;
}
return false;
}
void StringSet::clear_set(void)
{
set.clear();
}
int StringSet::no_of_strings(void) const
{
return set.size();
}
ostream& operator <<(ostream& outs, StringSet& the_strings)
{
vector<string>::const_iterator const_iter = the_strings.set.begin();
for( ; const_iter != the_strings.set.end(); const_iter++)
{
cout << *const_iter << " ";
}
cout << endl;
return outs;
}
//This function returns the union of the two string sets.
StringSet operator *(const StringSet& first, const StringSet& second)
{
vector<string> new_string_set;
new_string_set = first.set;
for( unsigned int i = 0; i < second.set.size(); i++)
{
vector<string>::const_iterator const_iter = find(new_string_set.begin(), new_string_set.end(), second.set[i]);
//String is new - include it.
if( const_iter == new_string_set.end() )
{
new_string_set.push_back(second.set[i]);
}
}
StringSet the_set(new_string_set);
return the_set;
}
//This method returns the intersection of the two string sets.
StringSet operator +(const StringSet& first, const StringSet& second)
{
//For each string in the first string look though the second and see if
//there is a matching pair, in which case include the string in the set.
vector<string> new_string_set;
vector<string>::const_iterator const_iter = first.set.begin();
for ( ; const_iter != first.set.end(); ++const_iter)
{
//Then search through the entire second string to see if
//there is a duplicate.
vector<string>::const_iterator const_iter2 = second.set.begin();
for( ; const_iter2 != second.set.end(); const_iter2++)
{
if( *const_iter == *const_iter2 )
{
new_string_set.push_back(*const_iter);
}
}
}
StringSet new_set(new_string_set);
return new_set;
}
double StringSet::binary_coefficient( const StringSet& the_second_set)
{
double coefficient;
StringSet intersection = the_second_set + set;
coefficient = intersection.no_of_strings() / sqrt((double) no_of_strings()) * sqrt((double)the_second_set.no_of_strings());
return coefficient;
}
However when I try and calculate the coefficient using the following main function:
// Exercise13.cpp : main project file.
#include "stdafx.h"
#include <boost/regex.hpp>
#include "StringSet.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace boost;
//This function takes as input a string, which
//is then broken down into a series of words
//where the punctuaction is ignored.
StringSet break_string( const string the_string)
{
regex re;
cmatch matches;
StringSet words;
string search_pattern = "\\b(\\w)+\\b";
try
{
// Assign the regular expression for parsing.
re = search_pattern;
}
catch( regex_error& e)
{
cout << search_pattern << " is not a valid regular expression: \""
<< e.what() << "\"" << endl;
exit(1);
}
sregex_token_iterator p(the_string.begin(), the_string.end(), re, 0);
sregex_token_iterator end;
for( ; p != end; ++p)
{
string new_string(p->first, p->second);
String^ copy_han = gcnew String(new_string.c_str());
String^ copy_han2 = copy_han->ToLower();
char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(copy_han2);
string new_string2(str2);
words.add_string(new_string2);
}
return words;
}
int main(array<System::String ^> ^args)
{
StringSet words = break_string("Here is a string, with some; words");
StringSet words2 = break_string("There is another string,");
cout << words.binary_coefficient(words2);
return 0;
}
I get an index which is 1.5116 rather than a value from 0 to 1.
Does anybody have a clue why this is the case?
Any help would be appreciated.
You need more parentheses in the final calculation. a / b * c is parsed as (a / b) * c, but you want a / (b * c).
Maybe it's just a precedence matter
coefficient = intersection.no_of_strings() / sqrt((double) no_of_strings()) * sqrt((double)the_second_set.no_of_strings());
doesn't specify that you have to first multiply, then divide. Their precedence is the same but I'm not sure about choosen behaviour.. did you try specifying it:
coefficient = intersection.no_of_strings() / (sqrt((double) no_of_strings()) * sqrt((double)the_second_set.no_of_strings()));