I'm trying to sort a vector of objects so that objects with the same properties are right next to each other. Task is a class that takes and holds 3 pointers to some data somewhere.
vec.push_back(Task(&propertyOne, &propertyTwo, &propertyThree));
If I have a vector full of these, I want to sort them so that any Task that has equal propertyOne, propertyTwo, and propertyThree are put next to each other in the vector. If the two Task objects properties are not equal, it doesn't really matter where they go. Although it would be nice for them to go next to the Task objects that they have the most in common with.
How do I do this? I've tried the obvious (return a.propertyOne == b.propertyOne) but that didn't seem to work at all, and also it only compares the first property.
Best solution is use a tuple<property*, property*, property*> not a Task. This comes with comparison operators defined so you could simply do: vec.push_back(make_tuple(&propertyOne, &propertyTwo, &propertyThree)) for each entry into vec, then to sort just do:
sort(vec.begin(), vec.end())
If Task must be more elaborate than a tuple<property*, property*, property*>, you should define comparison operators for Task:
bool Task::operator< (const Task& rhs) {
return make_tuple(a, b, c) < make_tuple(rhs.a, rhs.b, rhs.c);
}
bool Task::operator== (const Task& rhs) {
return a == rhs.a && b == rhs.b && c == rhs.c;
}
Once these two are defined you can again do:
sort(vec.begin(), vec.end())
Let's task have three properties a, b, c for short. To sort such tasks in correct order you have to do something like this:
std::sort(v.begin(), v.end(), [](const Task& l, const Task& r) {
if (l.a == r.a) {
if (l.b == r.b) {
return l.c < r.c;
}
return l.b < r.b;
}
return l.a < r.a;
});
I'd do it using a simple lambda containing a couple of ternary operators
std::sort(v.begin(), v.end(), [](const Task& l, const Task& r)
{
return (l.a == r.a) ?
((l.b == r.b) ? (l.c < r.c) : (l.b < r.b)) :
(l.a < r.a);
});
This obviously assumes the properties a and b can be compared using ==, and that all three properties can be compared using <.
Alternatively, you might want to supply appropriate specialisations of functionals from <functional> (std::equal_to, std::less, etc) and use those instead.
Related
I have this structure
struct Event {
const string event;
const int order;
Event(const string& _event, const int& _order):event(_event),order(_order) {}
};
struct EventCompare {
bool operator()(const Event& lhs, const Event& rhs)
{
return (lhs.order < rhs.order);
}
};
which I would like to use in a set:
set<Event, EventCompare> events;
I do know that sets doesn't allow duplicates. However, I would like to define duplicates as two instance of structs with equal events regardless of their orders, in other words, A = B iff A.event == B.event. This definition has to affect the way the set works, which means that, for example, the set has to ignore Event("holiday", 1), if it already contains Event("holiday", 0).
How can I do that? I've tried to add
if (lhs.event == rhs.event)
return false;
in my EventCompare, but that didn't work. Will using pair instead of struct help anyhow?
If under the conditions you specified they are considered to be equal, then it's obvious that the result of the < comparison would be false. One is not less than the other, they are considered to be equal. The comparison operator, for the purpose of being used with associative containers, only needs to indicate if one instance is "less" than the other. Since, under these circumstances, they are considered to be equal, neither one is less than the other.
Therefore:
struct EventCompare {
bool operator()(const Event& lhs, const Event& rhs)
{
if (lhs.event == rhs.event)
return false;
return (lhs.order < rhs.order);
}
};
However, this does not address the situation where two instances of the Event object have the same order, but different Events. If such situation is cannot arise, you don't have to worry about it. If it can, simply decide what their ordering would be, and set the return value of the comparison operator, in that case, accordingly.
The closest you can use is:
struct EventCompare {
bool operator()(const Event& lhs, const Event& rhs)
{
if (lhs.event == rhs.event)
return false;
return (lhs.order < rhs.order);
}
};
However, the compare criteria you are asking for does not meet the strictly weak ordering, which is required to put objects in a std::set.
Let's you have three objects with the following data:
obj1 = {"foo", 200}
obj2 = {"bar", 300}
obj3 = {"foo", 400}
If you add objects to the set in the order obj1, obj2, obj3, you will see only obj1 and obj2, in that order, in the set.
If you add objects to the set in the order obj2, obj3, obj1, you will see only obj2 and obj3, in that order, in the set.
Not only do you get different objects in the set depending on which object is added to the set first but even the objects appear in different order based on which object was added to the set first. I can only see problems in the future if you follow this strategy.
I think you should take a fresh look at your requirements and look for a cleaner solution. I am not able to suggest a solution without a deeper understanding of what your are trying to do.
A set doesn't look for equality. It only checks for an ordering.
Since you want two events to be equal if the event is the same, that means that neither comes before the other, and your comparison function should return false in that case.
bool operator()(const Event& lhs, const Event& rhs) const {
return lhs.event != rhs.event && lhs.order < rhs.order;
}
However, this won't work since it no longer defines a strict weak ordering, since you can have events where A < B and B < C but !(A < C) if A and C have matching event strings but B's order is between A's and C's.
So no, you can't use a set to store elements where a 2nd non-ordering attribute overrides the ordering one. You'd have to change the ordering to be based on event, but then you won't be able to look things up based on the order.
You could use a map to map the event strings to the order value used to store them into the set. Then you check the map to see if it is already there, and decide which element to keep in the set. Otherwise update both the set and map with the new entry.
I seem to be missing something blindingly obvious. But surely all you need to do is compare according to your requirements?
struct EventCompareName
{
bool operator()(const Event& lhs, const Event& rhs)
{
// If you want to compare by event and not order, then do so.
return (lhs.event < rhs.event);
// Rather than comparing by order and not event.
//return (lhs.order < rhs.order);
}
};
std::set<Event, EventCompareName> events;
Of course, you might also want to compare by order in some cases (even though your question gives absolutely zero indication of that requirement). In which case:
struct EventCompareNameOrder
{
bool operator()(const Event& lhs, const Event& rhs)
{
if (lhs.event != rhs.event)
return (lhs.event < rhs.event);
return (lhs.order < rhs.order);
}
};
std::set<Event, EventCompareNameOrder> allEvents;
I have no idea how to implement priority_queue, when comparing an objects integer field , it's is clear.
For example
bool operator()(const Toast &t1, const Toast &t2) const
{
int t1value = t1.bread * 1000 + t1.butter;
int t2value = t2.bread * 1000 + t2.butter;
return t1value < t2value;
}
This will place the objects in the heap according to the values.
The question is how to compare objects according to the bool fields? How to store several objects according to the boolean type?
For example :
vip=true, notvip=false;
Vip1 , notVip2, Vip3.
The result should be: Vip1,Vip3,notVip2;
Can you give me an idea.
Your question is a bit unclear. I'm going to assume that the structure that you want to sort is Toast.
If you simply want to prioritize Toasts based on a bool, say status, your comparison object is pretty easy:
class mycomparison
{
public:
bool operator() (const Toast& lhs, const Toast& rhs) const
{
return lhs.status < rhs.status;
}
};
Now I'm going to assume the following to expand on this, given Toast has the following bool members listed in order of most significant to least significant: vip, notvip, Vip1,Vip3, andnotVip2`, I'm also assuming that the comparison on "not" members should be inverted:
class mycomparison
{
public:
bool operator() (const Toast& lhs, const Toast& rhs) const
{
return lhs.vip < rhs.vip || lhs.vip == rhs.vip && // return true if rhs.vip is the only true or continue to test
( lhs.noVip > rhs.noVip || lhs.noVip == rhs.noVip && // return true if rhs.noVip is the only false or continue to test
( lhs.Vip1 < rhs.Vip1 || lhs.Vip1 == rhs.Vip1 && // return true if lhs.Vip1 is less than rhs.Vip1 or continue to test
( lhs.Vip3 < rhs.Vip3 || lhs.Vip3 == rhs.Vip3 && // return true if lhs.Vip3 is less than rhs.Vip3 or continue to test
lhs.notVip2 > rhs.notVip2 ) ) ); // return true if lhs.notVip2 is greater than rhs.notVip2 or return false
}
};
Note that either of these mycomparison classes works fine if the members are bools or any other type for which operator<, operator> and operator== is defined. To use mycomparision in a std::priority_queue you'll just need to pass it as the comparison object in std::priority_queue's ctor, for example: std::priority_queue< Toast > foo( mycomparison() );
I'm trying to sort some enums into ascending order using the sort function (sort(cards.begin(), cards.end());) but it doesn't seem to be sorting them correctly. It is returning them in the order:
card3 {rank=SEVEN (7) suit=CLUBS (0) }
card4 {rank=TWO (2) suit=HEARTS (2) }
card5 = {rank=NINE (9) suit=SPADES (3) }
I suspected it was a problem with my comparison operator overloading, which is this:
inline bool operator<(const Card& c1, const Card& c2)
{
return c1.getRank() < c2.getRank() &&
c1.getSuit() < c2.getSuit();
}
as far as I can see, only the right hand side of that comparison is true when comparing card3 and card4 and so it should be returning false but when I card3<card4 it returns true!
I'm probably hideously misunderstanding logic here, but this doesn't make any sense to me....
Thanks for your time :)
Your comparison operator does not implement a strict weak ordering, which is a requirement for std::sort. A simple way to implement this is with a lexicographical comparison. This is made easy with std::tie:
#include <tuple> // for std::tie
inline bool operator<(const Card& c1, const Card& c2)
{
return std::tie(s1.getRank(), c1.getSuit()) <
std::tie(c2.getRank(), c2.getSuit());
}
You can also roll out your own in the absence of std::tie. The idea is that you have to handle the case where both ranks are the same.
Typically when you compare things with two items you do it like this:
inline bool operator<(const Card& c1, const Card& c2)
{
if(c1.getSuit() == c2.getSuit()) {
return c1.getRank() < c2.getRank()
} else {
return c1.getSuit() < c2.getSuit();
}
}
Edit... I didn't think of the actual resulting order... I guess you want to order first by Suit and then by Rank... so I've just reverted the order in my original post.
I am writing some code where I am storing lots of objects that I want to get back based on set criteria. So to me it made sense to use a map with an object as a key. Where the object would contain the "set criteria".
Here is a simplified example of the kind of objects i am dealing with:
class key
{
int x,y,w,h;
}
class object
{
...
}
std::map<key, object, KeyCompare> m_mapOfObjects;
Quite simple, the first thought was to create a compare functions like this:
struct KeyCompare
{
bool operator()(const key &a, const key &b)
{
return a.x < b.x || a.y < b.y || a.w < b.w || a.h < b.h;
}
}
but then i thought the chances of this returning true are quite high. So I figured this would lead to a very unbalanced tree and therefore slow searching.
My main worry is that as I understand it, std::map uses that one function in this way:
if( keyCompare(a,b) )
{
//left side
}
else if (keyCompare(b,a))
{
//right side
}
else
{
//equal
}
So i can't just use a.x < b.x, because then anything with the same x would be considered equal, which is not what i want. I would not mind it ordering it in this way but its the "equal" bit i just can't seem to solve without making it unbalanced.
I figure multiplying them all together is a no no for obvious reasons.
So the only solution i could come up with was to create a "UID" base on the info:
typedef long unsigned int UIDType;
class key
{
private:
UIDType combine(const UIDType a, const UIDType b)
{
UIDType times = 1;
while (times <= b)
times *= 10;
return (a*times) + b;
}
void AddToUID(UIDType number)
{
if(number < m_UID)
{
m_UID = combine(number, m_UID);
}
else
{
m_UID = combine(m_UID, number);
}
}
UIDType UID;
public:
int x,y,w,h;
key()
{
AddToUID(x);
AddToUID(y);
AddToUID(w);
AddToUID(h);
}
}
struct KeyCompare
{
bool operator()(const key &a, const key &b)
{
return a.UID < b.UID;
}
}
But not only does that feel a little hacky, "long unsigned int" isn't big enough to hold the potential numbers. I could put it in a string, but speed is an issue here and I assumed an std::string < is expensive. Overall though the smaller i can make this object the better.
I was wondering if anyone has any suggestions for how to do this better. Perhaps i need to use something other then a std::map or perhaps there is another overload. Or perhaps there is something glaringly obvious that i'm missing here. I really feel like i'm over-complicating this, perhaps im really barking up the wrong tree with a map.
As i was writing this it occurs to me that divide is another way to get a "unique" number but that could also equal very large numbers
All you need is to implement a strict weak ordering, which you can easily achieve using std::tie, which has a less than comparison operator< which performs a lexicographical comparison:
#include <tuple>
struct KeyCompare
{
bool operator()(const key& a, const key& b) const
{
return std::tie(a.x, a.y, a.w, a.h) < std::tie(b.x, b.y, b.w, b.h);
}
}
If you do not have the required C++11 support, you can use std::tr1::tie from <tr1/tuple> or equivalent versions from the boost libraries.
I feel juanchopanza has a very good solution, for those who do not have C++11 support or boost libraries
I found a very simple solution on:
What's the simplest way of defining lexicographic comparison for elements of a class?
This solution works for my particular problem a little better then tuple would (as i also have an array of values that i would like to consider). But I would highly recommend considering tuple in future, as will I.
struct keyCompare
{
bool operator()(const key &a, const key&b)
{
if(a.x != b.x) return a.x < b.x;
if(a.y != b.y) return a.y < b.y;
if(a.w != b.w) return a.w < b.w;
if(a.h != b.h) return a.h < b.h;
return false; //they must be equal
}
}
thanks to juanchopanza for his answer and to anyone else who had a look in
I have an
stl::map that has the key defined as an object I defined, and int. The use of the map is as follows:
I have a list of the specific object and I want to count how many identical objects I have. So I insert the objects into the map.if the object already exists in the map I increase it's value (hence the counter). The object has all the basic operators defined. The object consist of 5 strings. The == operator defined as the comparison of all 5 strings, and logically is meaningfull in the context. The problem is that the operator < has no logic meaning in the context. I care only if the objects are equal. I can't define which of two different objects is bigger.so for the sake of stl map defined this operator as the result of if else ladder and in each if I compared with "<" another string of the five. If true return true else, if.... And the last else returns false. In a specific case of the object , where I had three identical instances, I got the map containing two identical objects as keys, one of them had the counter of 1 and the other had 2.
i can't understand what is the problem and how could it happen.
For those who requested some code examples - for reason i can't explain - i can't post the code itself, but i will write a good example of it(please ignore little things like missing ';' - i wrote it in 5 minutes):
class Example
{
private:
string one;
string two;
string three;
string four;
string five;
public:
inline Example (string a_one,string a_two, string a_four, string a_five) :
one(a_one),two(a_two),three(a_three),four(a_four),five(a_five)
{}
inline bool operator == (const Example& other) const
{
if (one == other.one)
{
if (two == other.two)
{
if (three == other.three)
{
if (four == other.four)
{
if (five == other.five)
{
return true;
}
}
}
}
}
return false;
}
inline bool operator < (const Example& other) const
{
if (one < other.one)
{
return true;
}
else if (two < other.two)
{
return true;
}
else if (three < other.three)
{
return true ;
}
else if (four < other.four)
{
return true;
}
else if (five < other.five)
{
return true;
}
else
{
return false;
}
}
}
void CountExample(Example& example,std::map<Example,int>& counters);
void main()
{
std::map<Example,int> counters;
std::list<Example> examples = GetExamples();
//GetExamples defined elsewhere, and initializes examples with a long list of instances of Example
std::list<Example>::const_iterator Iter;
for (Iter = examples.begin();Iter != examples.end();Iter++)
{
CountExample(*Iter);
}
PrintCounters(counters);//PrintCounters defined elsewhere and prints the map to a file
}
void CountExample(Example& example,std::map<Example,int>& counters)
{
std::map<Example,int>::const_iterator Iter;
Iter = counters.find(example);
if (Iter ==counters.end()) //means the specific Example is not in the map
{
counters.insert(std::pair<Example,int>(example,1));
}
else
{
counters[example] += 1;
{
}
If you have a reasonably modern compiler, that ladder of comparisons can be replaced with a single comparison between two std::tie()'d tuples:
#include <tuple>
...
bool operator== (const Example& other) const
{
return std::tie(one, two, three, four, five)
== std::tie(other.one, other.two, other.three, other.four, other.five);
}
bool operator < (const Example& other) const
{
return std::tie(one, two, three, four, five)
< std::tie(other.one, other.two, other.three, other.four, other.five);
}
Incidentally, it may be simpler to use a std::multiset to count the number of times a particular element is stored in an associative container, that simplifies CountExample to a one-liner
void CountExample(const Example& example, std::multiset<Example>& counters)
{
counters.insert(example);
}
Although printing becomes a bit more tricky:
void PrintCounters(const std::multiset<Example>& counters)
{
for(auto i=counters.begin(); i!=counters.end(); i = counters.upper_bound(*i))
std::cout << *i << ":" << counters.count(*i) << '\n';
}
Test on ideone: http://ideone.com/uA7ao
To make a comparison with multiple elements, each element that you compare will have three outcomes: less than, greater than, or equivalent. You must account for all of these cases.
bool LessThan(const MyClass & left, const MyClass right)
{
if (left.one < right.one)
return true;
else if (right.one < left.one)
return false;
// equivalent in one
if (left.two < right.two)
return true;
else if (right.two < left.two)
return false;
// equivalent in one and two
...
return false;
}
You need to provide an operator< for you type. This can be pretty tedious to write, but you can simply it by using a Boost.Tuple - that way, the tuple handles the comparisons, leaving your code easier to read, write and understand.
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <string>
struct Object
{
std::string a;
std::string b;
std::string c;
};
bool operator<(const Object& obj1, const Object& obj2)
{
return (boost::tie(obj1.a, obj1.b, obj1.c) <
boost::tie(obj2.a, obj2.b, obj2.c));
}
Edit: After thinking about the problem some more, I've decided to removed my older answer since it did not seem pertinent to the current problem being experienced. Your operator< method does seem to be fulfilling the requirements for a strict weak ordering, so I think the problem lies somewhere else, and so I'm leaving the following alternate solution below ...
It seem you're having issues creating a total order for your map, so you might want to look at std::unordered_map as an alternative that will directly apply your operator== for detecting equality, rather than using your operator< for a strict weak ordering ... you'll have to provide a hash-function for your class, but otherwise the use of the hash-table based std::unordered_map container is pretty straight-forward.