C++ STL priority_queue , the way to compare objects - c++

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() );

Related

How to consider two structs equal regardless of their second attribute?

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;

std::sort equal objects with multiple properties

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.

C++ “invalid comparator” error in operator overload when negating the result

I ran into very strange problem regarding operator overload. The function below works fine outside the if branch. When I attempt to negate the result of comparison I always get “invalid comparator” error.
Negating using ! does not work, same with ternary operator. Why is this happening?
bool operator<(const Team& firstTeam, const Team& secondTeam)
{
if (firstTeam.getId() > 8) {
return !( firstTeam.getId() < secondTeam.getId());
}
return firstTeam.getId() < secondTeam.getId();
}
Suppose there is team a with id 9 and team b with id 7, then b < a (outside of if) and a < b (inside if) are both true
Correct comparator should define strict weak ordering
It's not 100% clear what are you trying to achieve, but the following code will define correct strict weak ordering
bool operator<(const Team& firstTeam, const Team& secondTeam)
{
if (firstTeam.getId() > 8 && secondTeam.getId() > 8) {
return firstTeam.getId() > secondTeam.getId(); // note > instead of <
}
return firstTeam.getId() < secondTeam.getId();
}

Parsing logic expressions in strings with known variables

What I want to do is create a function that would take an input string with 1-3 variables (and it would require at least one), then parse that input for an expression. This expression would then be considered logical and return true or false.
For example,
"X == Y && 0.1 <= X < 1.0 && Z > 0.0"
Then first the variables in that string would be iterated through and replaced with actual values. After all of the variables have been replaced the modified string would be like this:
"1.0 == 1.0 && 0.1 <= 1.0 < 1.0 && 0.6 > 0.0"
Then that string would be logically tested. If it is true the function would return true, else false.
I was thinking the function would be something like this:
IsLogical(string, float X, float Y, float Z)
I've been searching for the past few hours for ways to do this and all I have found were arithmetic math equation parsers. So far all I have got done is the part where the variables are replaced with the three input values.
So if anyone is willing to help, can you tell me how it would be done? Showing me examples would definitely be great too.
This function can't use any external resources like boost or embedded Lua.
EDIT: This should include all the basic comparison operators in C++, such as less than, more than, less than or equal to, more than or equal to, equal to, not equal to, and, or, parenthesis. It would all be in C++ format. If the format of the input isn't correct the function would automatically return false.
This is a wild stab at your problem. I'm not going to code it in detail, and I'm not focusing on language semantics, perfect compilation etc.
Wrap your variables with a new type (ExpresionVar). Register each variable with an ExpressionBuilder, that contains logical operators registered in the beginning of the program. Refer to the comments (psuedo code) in result...
ExpressionVar
{
float value_;
std::string name_;
};
LogicalOp
{
virtual std::string id() const = 0;
virtual bool operator()(
const ExpressionVar& lhs, const ExpressionVar& rhs) const = 0;
};
IsEqual : LogicalOp
{
std::string id() const override{ return "=="; }
bool operator()(
const ExpressionVar& lhs, const ExpressionVar& rhs) const override
{
returns lhs.value_ == rhs.value_;
}
};
//etc...
ExpressionBuilder
{
void registerVar(std::initializer_list<const ExpressionVar&>);
static void registerLogical(LogicalOp&&);
bool result(const std::string& expression) const
{
//run through expression.
// Search for known ExpressionVariable (EV), if variable found,
// store as lhs, else throw.
// Then search for LogicalOp until found, if another EV found
// throw, else store op, whereafter searching of rhs, etc...
}
};
bool IsLogical(
const std::string& input,
std::initializer_list<ExpressionVar> variables)
{
//Logical ops registered prior...
return ExpressionBuilder().registerVar(variables).result(input);
}

Double map insertion problem

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.