my code is like this:
struct Info
{
string name;
int score;
bool operator< (const Info &x) const
{
return score < x.score;
}
};
int main(int argc, char *argv[])
{
Info a, b;
a.name = "eric";
a.score = 90;
b.name = "cat";
b.score = 85;
map<Info, int> m;
m[a] = 1;
m[b] = 2;
map<Info, int>::iterator it;
for(it = m.begin(); it != m.end(); it++)
{
cout << it->first.name << endl;
}
return 0;
}
it prints out "cat" and "eric", as expected. but how ever, when I modify it to (make a.score and b.score the same)
Info a, b;
a.name = "eric";
a.score = 90;
b.name = "cat";
b.score = 90;
It prints out "eric" only, there's only one element in the whole map.
question: does std::map think they are the same key? how do I make std::map think they are not the same key? I tried operator==, but not working.
They are the same key because your custom comparator uses the score as the key. Try this instead
bool operator< (const Info &x) const
{
return name < x.name;
}
If you want the name to be the key but the map to be sorted on the score, then I'm afraid you are out of luck, because maps are sorted on the key by definition. You'll have to pick another data structure or another algorithm.
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 have following problem. My vector contains pairs of pairs (see example below).
In the example below I will push_back vector with some "random" data.
What will be best solution to delete the vector element if any of their values will be equal i.e. 100 and update value if less than 100.
i.e.
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
MyMap pair1;
MyMap pair2;
In first example I want to update this pair because pair1.first is less than 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 101;
pair2.second = 101;
In second example I want to delete this pair because pair2.first is equal to 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 100;
pair2.second = 101;
Using functor "check" I am able to delete one or more elements (in this example just one).
It is possible to increase every value of that pair by 1 using std::replace_if function?
Is there any function that will update this value if any of these values will be lower then "X" and delete if any of these values will be equal "X"?
I know how to do it writing my own function but I am curious.
#include "stdafx.h"
#include<algorithm>
#include<vector>
#include<iostream>
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
void PrintAll(std::vector<MyPair> & v);
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size);
class check
{
public:
check(int c)
: cmpValue(c)
{
}
bool operator()(const MyPair & mp) const
{
return (mp.first.first == cmpValue);
}
private:
int cmpValue;
};
int _tmain(int argc, _TCHAR* argv[])
{
const int size = 10;
std::vector<MyPair> vecotorOfMaps;
FillVectorWithSomeStuff(vecotorOfMaps, size);
PrintAll(vecotorOfMaps);
std::vector<MyPair>::iterator it = std::find_if(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
if (it != vecotorOfMaps.end()) vecotorOfMaps.erase(it);
PrintAll(vecotorOfMaps);
system("pause");
return 0;
}
std::ostream & operator<<(std::ostream & stream, const MyPair & mp)
{
stream << "First:First = " << mp.first.first << " First.Second = " << mp.first.second << std::endl;
stream << "Second:First = " << mp.second.first << " Second.Second = " << mp.second.second << std::endl;
stream << std::endl;
return stream;
}
void PrintAll(std::vector<MyPair> & v)
{
for (std::vector<MyPair>::iterator it = v.begin(); it != v.end(); ++it)
{
std::cout << *it;
}
}
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size)
{
for (int i = 0; i < size; ++i)
{
MyMap m1(i + i * 10, i + i * 20);
MyMap m2(i + i * 30, i + i * 40);
MyPair mp(m1, m2);
v.push_back(mp);
}
}
Use std::stable_partition, along with std::for_each:
#include <algorithm>
//...partition the elements in the vector
std::vector<MyPair>::iterator it =
std::stable_partition(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
//erase the ones equal to "check"
vecotorOfMaps.erase(vecotorOfMaps.begin(), it);
// adjust the ones that were left over
for_each(vecotorOfMaps.begin(), vecotorOfMaps.end(), add(1));
Basically, the stable_partition places all the items you will delete in the front of the array (the left side of the partiton it), and all of the other items to the right of it.
Then all that is done is to erase the items on the left of it (since they're equal to 100), and once that's done, go through the resulting vector, adding 1 to eac
struct info{
int a;
int b;
double c;
double d;
int e;
};
set<info> infoSet;
info information;
information.a = 1;
information.b = 1;
information.c = 1;
information.d = 1;
information.e = 1;
infoSet.insert(information);
information.a = 2;
information.b = 2;
information.c = 2;
information.d = 2;
information.e = 2;
infoSet.insert(information);
typedef pair<int, int> pairs;
pairs p;
p.first = 1; p.second = 1;
set<info>::iterator it;
it.find(??)
c,d and e are depend on a and b in info struct(kind of super key in DB).
I want to find iterator of set which has struct members a and b exactly same as p.first and second. and want to print it
which code do I have to put into (??) ?
you can do it like that
first way:
set<info> infoSet
for_each(infoSet.begin(),infoSet.end(),bind2nd(ptr_fun(compare), pairs);
//compare fun
void compare(info, pair<int, int>)
{
..... you can compare them here
}
for_each is just like "for(..)", so the second way is
set<info>::iterator it = infoSet.begin();
for(it; it!=infoSet.end(); it++)
{
if(it.first == p.first)
{
...// do what you want
break;
}
}
If you want to store your struct in a set, you need to provide a compare predicate for it.
I suppose that you want to make your struct as simple as possible, so I make the predicate and a convert function outside of your struct.
Example as follows:
struct Info
{
int a;
int b;
double c;
double d;
int e;
};
struct CompareInfo
{
bool operator() (const Info& lhs, const Info& rhs) const
{
return (lhs.a < rhs.a) && (lhs.b < rhs.b); // you can define your own rule here
}
};
typedef std::pair<int, int> Pairs;
Info MakeInfo(Pairs p) // convert from Pairs to Info
{
Info i = {p.first, p.second, 0, 0, 0}; // not sure what are values of c, d, e
return i;
}
std::set<Info, CompareInfo> infos;
Info info1 = {1,1,1,1,1};
infos.insert(info1);
Info info2 = {2,2,2,2,2};
infos.insert(info2);
Pairs p(1,1);
auto it = infos.find(MakeInfo(p));
if (it != infos.end())
{
// you found it!
}
I have the following comparator for string objects
struct Comparator{
int x;
bool operator() (string a, string b) {
int i = 1;
if(a < b) {
i = -1;
}
i*= x;
if(i==-1) {
return true;
}
return false;
}
};
As you can see, it has a parameter x. when it is = 1 the comparison of strings is normal and when it is =-1 the comparison is inverted.
When using it in a method like sort for vector elements I can give the object instance of this comparator with the right x, but when I want to give this comparator to a template class like set, I need to give a class and not an object instance. So the only solution I see is to make x static. Is there a better solution?
Here is the example of a main where I would like to use this comparator:
int main(int argc, char** argv)
{
vector<string> vec;
vec.push_back("c");
vec.push_back("a");
vec.push_back("b");
Comparator comp;
comp.x = 1; // for normal comparison
comp.x = -1; // for inverse comparison
sort(vec.begin(),vec.end(), comp); // here I can give the functor instance
for(vector<string>::iterator it = vec.begin() ; it != vec.end(); it++)
{
cout << *it << endl;
}
set<string, Comparator> ss; // but here I must give the name of the functor class
ss.insert("c");
ss.insert("a");
ss.insert("b");
for(set<string>::iterator it = ss.begin() ; it != ss.end(); it++)
{
cout << *it << endl;
}
return 0;
}
All the relevant constructors of set also take an instance of Comp class.
set<string, Comparator> ss(Comparator(-1));
Now you only need a constructor for Comparator that initializes its member x with an appropriate value.
That said, the standard library already comes with a comparator class for this purpose:
set<string, std::greater<std::string> > ss;
That will not work: a < b does not(!) mean b < a.
You might utilize std::string::compare:
bool operator() (const std::string& a, const std::string& b) {
int result = a.compare(b) * x;
return result < 0;
}
I have a std::map and trying to fill it with pairs (name, id). The id field is simply generated from map's size(). Here's a simplified version:
#include <iostream>
#include <map>
struct A {
std::string name;
int id;
A(const std::string &s) : name(s), id(-1) { }
};
class Database {
std::map<std::string, int> ids;
public:
void insert(A *item) {
ids[item->name] = item->id = ids.size();
}
void dump() const {
for (std::map<std::string, int>::const_iterator i = ids.begin(); i != ids.end(); i++)
std::cout << i->second << ". " << i->first << std::endl;
}
};
int main(int argc, char **agrv) {
A a("Test");
Database db;
db.insert(&a);
db.dump();
return 0;
}
The problem is that different compilers treat the ids[item->name] = item->id = ids.size() part differently. Clang++ produces
item->id = ids.size(); // First item gets 0
ids[item->name] = item->id;
when g++ does something like
ids.insert(std::pair<std::string, int>(item->name, 0));
item->id = ids.size(); // First item gets 1
ids[item->name] = item->id;
So, is this code valid (from the STL perspective) or it is as evil as i = ++i + ++i?
ids[item->name] = item->id = ids.size();
Without a sequence point separating the two calls, the compiler is free to evaluate operator[] and size() in any order it likes. There is no guarantee that size() will be called before operator[].
change ids[item->name] = item->id = ids.size(); to
item->id = ids.size();
ids[item->name] = item->id;