C++ map pointer variable sorting - c++

Hello I wanna know how to sort the map which Tkey variable is pointer type.
There is getName function which return char* type. so I tried to compare with strcmp. But there are some error in the return part.
struct Compare_P {
inline bool operator()(Person const& a, Person const& b) {
return (strcmp(a.getName(), b.getName())) < 0;
}
};
map<Person*, House*, Compare_P>A_List;

Your map's key is Person*, but the Compare_P::operator() takes Person const&. You can fix that by either defining
map<Person, House, Compare_P> A_List;
or by a correct Compare_P
struct Compare_P {
bool operator()(Person const* a, Person const* b) {
return (strcmp(a->getName(), b->getName())) < 0;
}

Related

C++: multimap custom comparator for object keys

Can I somehow use my own function for ordering the pairs in multimap? I have three classes CTimeStamp, CMail and CMailLog. And the thing is in the CMailLog I have
multimap<CTimeStamp, CMail> which I use because for this task I need solution which will be very fast for huge amounts of data and therefor I would need to somehow use method Compare from CTimeStamp when inserting into this multimap. The classes look something like this.
class CTimeStamp {
public:
int compare (const CTimeStamp &x) const;
...
}
class CMail {
...
}
class CMailLog {
public:
...
private:
multimap<CTimeStamp, CMail> logs;
}
I'm not sure how to do this or if it's even possible.
I would need to somehow use method Compare from CTimeStamp when inserting into this multimap
As from the std::multimap documentation, all you need is to either
provide a specialisation for std::less<CTimeStamp>
namespace std {
bool less<CTimeStamp>(const CTimeStamp& a, const CTimeStamp& b) {
return a.compare(b) < 0;
}
}
or
provide a custom comparator at the constructor:
CMailLog() :
logs([](const CTimeStamp& a, const CTimeStamp& b) { return a.compare(b) < 0; })
{}
I used a lambda expression in my last example for the constructor as I consider that's the shortest and most comprehensible form.
In fact any callable with the signature bool (const CTimeStamp&,const CTimeStamp&) would fit well.
You might also write a simple global function
bool foo(const CTimeStamp& a,const CTimeStamp& b) {
return a.compare(b) < 0;
}
or appropriate callable type
struct foo {
bool operator()(const CTimeStamp& a,const CTimeStamp& b) {
return a.compare(b) < 0;
}
};
and pass that one at the
multimap<CTimeStamp, CMail> logs;
in the constructor initializer list:
CMailLog() : logs(foo) {}
Callable struct version
CMailLog() : logs(foo()) {}

Can a map's key be shared with part of the value?

Can a std::map's or std::unordered_map's key be shared with part of the value? Especially if the key is non-trivial, say like a std::string?
As a simple example let's take a Person object:
struct Person {
// lots of other values
std::string name;
}
std::unordered_map<std::string, std::shared_ptr<Person>> people;
void insertPerson(std::shared_ptr<Person>& p) {
people[p.name] = p;
// ^^^^^^
// copy of name string
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
return people[name];
}
My first thought is a wrapper around the name that points to the person, but I cannot figure out how to do a lookup by name.
For your purpose, a std::map can be considered a std::set containing std::pair's which is ordered (and thus efficiently accessible) according to the first element of the pair.
This view is particularly useful if key and value elements are partly identical, because then you do not need to artificially separate value and key elements for a set (and neither you need to write wrappers around the values which select the key).
Instead, one only has to provide a custom ordering function which works on the set and extracts the relevant key part.
Following this idea, your example becomes
auto set_order = [](auto const& p, auto const& s) { return p->name < s->name; };
std::set<std::shared_ptr<Person>, decltype(set_order)> people(set_order);
void insertPerson(std::shared_ptr<Person>& p) {
people.insert(p);
}
As an alternative, here you could also drop the custom comparison and order the set by the addresses in the shared pointer (which supports < and thus can be used directly in the set):
std::set<std::shared_ptr<Person> > people;
void insertPerson(std::shared_ptr<Person>& p) {
people.insert(p);
}
Replace set by unordered_set where needed (in general you then also need to provide a suitable hash function).
EDIT: The lookup can be performed using std:lower_bound:
std::shared_ptr<Person> lookupPerson(std::string const& s)
{
auto comp = [](auto const& p, auto const& s) { return p->name < s; };
return *std::lower_bound(std::begin(people), std::end(people), s, comp);
}
DEMO.
EDIT 2: However, given this more-or-less ugly stuff, you can also follow the lines of your primary idea and use a small wrapper around the value as key, something like
struct PersonKey
{
PersonKey(std::shared_ptr<Person> const& p) : s(p->name) {}
PersonKey(std::string const& _s) : s(_s) {}
std::string s;
bool operator<(PersonKey const& rhs) const
{
return s < rhs.s;
}
};
Use it like (untested)
std::map<PersonKey, std::shared_ptr<Person> > m;
auto sptr = std::make_shared<Person>("Peter");
m[PersonKey(sptr)]=sptr;
Lookup is done through
m[PersonKey("Peter")];
Now I like this better than my first suggestion ;-)
Here's an alternative to davidhigh's answer.
struct Person {
// lots of other values
std::string name;
}
struct StrPtrCmp {
bool operator()(const std::string* a, const std::string* b) const {
return *a < *b;
}
}
std::map<const std::string*, std::shared_ptr<Person>, StrPtrCmp> people();
void insertPerson(std::shared_ptr<Person>& p) {
people[&(p.name)] = p;
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
return people[&name];
}
And a few edits to make it work with std::unordered_map:
struct StrPtrHash {
size_t operator()(const std::string* p) const {
return std::hash<std::string>()(*p);
}
};
struct StrPtrEquality {
bool operator()(const std::string* a, const std::string* b) const {
return std::equal_to<std::string>()(*a, *b);
}
};
std::unordered_map<const std::string*, std::shared_ptr<Person>, StrPtrHash, StrPtrEquality> people();

compare function in lower bound

I have following structure
enum quality { good = 0, bad, uncertain };
struct Value {
int time;
int value;
quality qual;
};
class MyClass {
public:
MyClass() {
InsertValues();
}
void InsertValues();
int GetLocationForTime(int time);
private:
vector<Value> valueContainer;
};
void MyClass::InsertValues() {
for(int num = 0; num < 5; num++) {
Value temp;
temp.time = num;
temp.value = num+1;
temp.qual = num % 2;
valueContainer.push_back(temp);
}
}
int MyClass::GetLocationForTime(int time)
{
// How to use lower bound here.
return 0;
}
In above code I have been thrown with lot of compile errors. I think I am doing wrong here I am new to STL programming and can you please correct me where is the error? Is there better to do this?
Thanks!
The predicate needs to take two parameters and return bool.
As your function is a member function it has the wrong signature.
In addition, you may need to be able to compare Value to int, Value to Value, int to Value and int to int using your functor.
struct CompareValueAndTime
{
bool operator()( const Value& v, int time ) const
{
return v.time < time;
}
bool operator()( const Value& v1, const Value& v2 ) const
{
return v1.time < v2.time;
}
bool operator()( int time1, int time2 ) const
{
return time1 < time2;
}
bool operator()( int time, const Value& v ) const
{
return time < v.time;
}
};
That is rather cumbersome, so let's reduce it:
struct CompareValueAndTime
{
int asTime( const Value& v ) const // or static
{
return v.time;
}
int asTime( int t ) const // or static
{
return t;
}
template< typename T1, typename T2 >
bool operator()( T1 const& t1, T2 const& t2 ) const
{
return asTime(t1) < asTime(t2);
}
};
then:
std::lower_bound(valueContainer.begin(), valueContainer.end(), time,
CompareValueAndTime() );
There are a couple of other errors too, e.g. no semicolon at the end of the class declaration, plus the fact that members of a class are private by default which makes your whole class private in this case. Did you miss a public: before the constructor?
Your function GetLocationForTime doesn't return a value. You need to take the result of lower_bound and subtract begin() from it. The function should also be const.
If the intention of this call is to insert here, then consider the fact that inserting in the middle of a vector is an O(N) operation and therefore vector may be the wrong collection type here.
Note that the lower_bound algorithm only works on pre-sorted collections. If you want to be able to look up on different members without continually resorting, you will want to create indexes on these fields, possibly using boost's multi_index
One error is that the fourth argument to lower_bound (compareValue in your code) cannot be a member function. It can be a functor or a free function. Making it a free function which is a friend of MyClass seems to be the simplest in your case. Also you are missing the return keyword.
class MyClass {
MyClass() { InsertValues(); }
void InsertValues();
int GetLocationForTime(int time);
friend bool compareValue(const Value& lhs, const Value& rhs)
{
return lhs.time < rhs.time;
}
Class keyword must start from lower c - class.
struct Value has wrong type qualtiy instead of quality
I dont see using namespace std to use STL types without it.
vector<value> - wrong type value instead of Value
Etc.
You have to check it first before posting here with such simple errors i think.
And main problem here that comparison function cant be member of class. Use it as free function:
bool compareValue(const Value lhs, const int time) {
return lhs.time < time ;
}
class is the keyword and not "Class":
class MyClass {
And its body should be followed by semicolon ;.
There can be other errors, but you may have to paste them in the question for further help.
You just want to make compareValue() a normal function. The way you have implemented it right now, you need an object of type MyClass around. The way std::lower_bound() will try to call it, it will just pass in two argument, no extra object. If you really want it the function to be a member, you can make it a static member.
That said, there is a performance penalty for using functions directly. You might want to have comparator type with an inline function call operator:
struct MyClassComparator {
bool operator()(MyClass const& m0, MyClass const& m1) const {
return m0.time < m1.time;
}
};
... and use MyClassComparator() as comparator.

Alphabetically sort a list of objects by member in C++

I'm trying to create a function to sort a list of contacts in an address book by name or last name.
void sortList (list<Contact> & address_book){
//define two iterators - first to point to the first element from the list, second to the second element
list<Contact>::iterator it = address_book.begin();
list<Contact>::iterator it2 = address_book.begin();
it2++;
//get the last name for the first 2 contacts
string last_name1 = it->get_last_name();
string last_name2 = it2->get_last_name();
int i = 0;
while (i < last_name1.length() && i < last_name2.length()){
if (last_name1[i] < last_name2[i]){
swap(it, it2);
break;
}
}
}
I'm sure I'm not doing it correctly, but I'm a little bit lost with these iterators. I also know I should have another while loop to loop through all my contacts until all of them are sorted but, honestly I have no idea how to implement it.
std::list has an overloaded member function sort, that
Sorts the elements in ascending order. The order of equal elements is guaranteed to be preserved. The first version uses operator< to compare the elements, the second version uses the given comparison function comp.
To give the comparison function you can use functors:
struct sort_by_name {
bool operator()(const Contact &a, const Contact &b)
{ return a.get_name() < b.get_name(); }
};
struct sort_by_last_name {
bool operator()(const Contact &a, const Contact &b)
{ return a.get_last_name() < b.get_last_name(); }
};
or simpler free functions
bool cmp_by_name(const Contact &a, const Contact &b)
{ return a.get_name() < b.get_name(); }
bool cmp_by_last_name(const Contact &a, const Contact &b)
{ return a.get_last_name() < b.get_last_name(); }
and you call it either
address_book.sort(sort_by_name());
address_book.sort(sort_by_last_name());
or
address_book.sort(cmp_by_name);
address_book.sort(cmp_by_last_name);
the accessors get_name() and get_last_name() must be const.
Don't do your own sorting. Use std::sort(). You'll need to supply a custom comparator — something like this:
struct LastNameComp {
bool operator()(const Contact& a, const Contact& b) const {
return a.get_last_name() < b.get_last_name();
}
}
⋮
std::sort(address_book.begin(), address_book.end(), LastNameComp());
If you have access to a C++11 compiler, you can do better:
std::sort(address_book.begin(), address_book.end(),
[](const Contact& a, const Contact& b) {
return a.get_last_name() < b.get_last_name();
});
Expanding on the answers given using std::lexicographical_compare and lists internal sort.
struct LastNameComp {
bool operator()(const Contact& a, const Contact& b) {
return std::lexicographical_compare(
a.get_last_name().begin(), a.get_last_name().end(),
b.get_last_name().begin(), b.get_last_name().end(),
);
}
};
address_book.sort(LastNameComp());

Insert a pair of object into a map

I'm trying to insert some pair value into a map. May map is composed by an object and a vector of another object. i don't know why but the only way to make the code to compile is to declare the first object like a pointer. But in this way when I insert some object, only the first pair is put into the map.
My map is this:
map<prmEdge,vector<prmNode> > archi;
this is the code:
{
bool prmPlanner::insert_edge(int from,int to,int h) {
prmEdge e;
int f=from;
int t=to;
if(to<from){
f=to;
t=from;
}
e.setFrom(f);
e.setTo(t);
vector<prmNode> app;
prmNode par=nodes[e.getFrom()];
prmNode arr=nodes[e.getTo()];
app.push_back(par);
app.push_back(arr);
archi.insert(pair<prmEdge,vector<prmNode> >(e,app) );
return true;
}
}
In this way, I have an error in compilation in the class pair.h.
What could I do?? Thank you very much.
You need to supply a comparator for prmEdge. My guess is that it uses the default comparator for map, e.g. comparing the address of the key -- which is always the same because e is local.
Objects that serve as Keys in the map need to be ordered, so you either need to supply a operator for comparing edges, or a comparator function for map.
class EdgeComparator {
public:
bool operator( )( const prmEdge& emp1, const prmEdge& emp2) const {
// ... ?
}
};
map<prmEdge,vector<prmNode>, EdgeComparator > archi;
The really hard part is deciding how to compare the edges so a definitive order is defined. Assuming that you only have from and to You can try with:
class EdgeComparator {
public:
bool operator( )( const prmEdge& emp1, const prmEdge& emp2) const {
if ( emp1.from != emp2.from )
return ( emp1.from < emp2.from );
return ( emp1.to < emp2.to );
}
};
It will sort on primary key from and secondary to.
The class prmEdge needs to define a comparison function (default is operator<) to work with std::map. Although you don't post that code, I would expect that to be your problem (for the record, pointer have an operator< defined.
struct A {
int a;
bool operator<(A other)
{
return a < other.a;
}
};
struct B {
int b;
};
bool cmp(B lhs, B rhs)
{
return lhs.b < rhs.b;
}
std::map<A, int> map_a;
std::map<B, int, std::pointer_to_binary_function<B, B, bool> > map_b(std::ptr_fun(cmp));
Map elements are ordered by their keys. But the map needs to know how:
Either overload the < operator in the prmEdge class...
class prmEdge
{
//...
public:
bool operator<(const prmEdge& right) const
{
//...
}
};
...or specify a comparator for the map:
class Comparator
{
public:
bool operator()(const prmEdge& left, const prmEdge& right) const
{
// ...
}
};
map<prmEdge, vector<prmNode>, Comparator> archi;