QMultiMap with struct as key and struct as value, comparison operators - c++

I have two structs, for example, first struct as key in QMultiMap:
struct KeyStruct
{
int keydata1;
int keydata2;
int keydata3;
bool operator < ( const KeyStruct& o ) const
{
return keydata1 < o.keydata1 || keydata2 < o.keydata2 || keydata3 < o.keydata3;
}
};
And second struct as value:
struct ValueStruct
{
int valuedata1;
QString valuedata2;
};
I tried to use QMultiMap like this:
multiMap.insert( keyStruct, valueStruct ); // in a loop
foreach( keyStruct, multiMap.keys()){
foreach( valueStruct, multiMap.values( keyStruct )){
// it never reaches this line
}
}
I'm shure in some cases I have multiple values per key, but can't even get one. I think the problem is in comparsion.
Also, I tried this:
bool operator == ( const KeyStruct& o ) const
{
return keydata1 == o.keydata1 &&
keydata2 == o.keydata2 &&
keydata3 == o.keydata3;
}
But it seems QMultiMap just doesn't use this code in my case.

Any data structure ( to my knowledge) that requires an operator<() to function has also the condition that:
A<B => ! B<A
This seems trivial, but in your case, it is possible that A<B && B<A. For instance if A.keydata1 < B.keydata1 but A.keydata2 > B.keydata2.
To fix this you should write your operator something like this:
bool operator < ( const KeyStruct& o ) const
{
if(keyData1 != o.keyData1) return keyData1 < o.keyData1;
if(keyData2 != o.keyData2) return keyData2 < o.keyData2;
//etc
return false; //they are equal
}

Related

C++ recursive struct comparator

I have created a struct to use as a key in a map to avoid having duplicate elements.
The struct contains pointers to children and siblings of its own type.
For the map, I have created a custom comparator that is supposed to recursively look at the element, the children and the siblings until a difference is found to make sure the elements are the same.
However, for some reason it is not working and Im still getting duplicates. After checking them out in the debugger, I concluded that they are indeed the exact same through and through so the problem must probably be somewhere in there.
This is the struct.
struct controlIdentifier
{
DWORD m_dwID;
DWORD m_dwDefaultID;
DWORD m_dwDisableID;
BYTE m_bType;
int m_nWidth;
int m_nHeight;
int m_nMargineH;
int m_nMargineV;
shared_ptr<controlIdentifier> m_pCHILD;
shared_ptr<controlIdentifier> m_pNEXT;
bool operator<(const controlIdentifier& id) const
{
if (m_dwDefaultID < id.m_dwDefaultID)
return true;
if (m_dwDisableID < id.m_dwDisableID)
return true;
if (m_bType < id.m_bType)
return true;
if (m_nWidth < id.m_nWidth)
return true;
if (m_nHeight < id.m_nHeight)
return true;
if (m_nMargineH < id.m_nMargineH)
return true;
if (m_nMargineV < id.m_nMargineV)
return true;
if (!m_pCHILD && id.m_pCHILD)
return true;
if (m_pCHILD && !id.m_pCHILD)
return false;
if (!m_pNEXT && id.m_pNEXT)
return true;
if (m_pNEXT && !id.m_pNEXT)
return false;
bool smaller = false;
if (m_pCHILD && id.m_pCHILD)
smaller = *m_pCHILD < *id.m_pCHILD;
if (!smaller)
{
if (m_pNEXT && id.m_pNEXT)
return *m_pNEXT < *id.m_pNEXT;
}
else
return smaller;
return false;
}
};
And this is how it's used.
struct cmpBySharedPtr {
bool operator()(const shared_ptr<controlIdentifier>& a, const shared_ptr<controlIdentifier>& b) const {
return *a < *b;
}
};
std::set<FRAMEDESC_SHAREDPTR> m_curFrames;
std::map<shared_ptr<controlIdentifier>, FRAMEDESC_SHAREDPTR, cmpBySharedPtr> m_serialFrames;
for (auto&& frame : m_curFrames)
{
shared_ptr<controlIdentifier> id;
makeIdentifiers(frame, id);
id->m_dwID = newId;
auto find = m_serialFrames.find(id);
if (find == m_serialFrames.end())
{
m_serialFrames.insert(std::pair(id, frame));
newId++;
}
}
m_dwID is not being compared on purspose.
Consider A = (child = 5, next = 6) and B = (child = 6, next = 5). Now A<B is true as (A.child < B.child) is true and it just returns that. Now consider B<A. B.child < A.child is false, so it checks the next fields.. Now B.next < A.next is true, so your comparison returns true.
So this is nonsensical -> A<B is true and B<A is true. This means your comparator is invalid.
The technical term for this is the comparator requires strict weak ordering - see https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. Your comparator breaks the asymmetry requirement.
You can construct operator < by comparing field by field. But what you did is too little. Basically it shall look like this:
bool operator < (const A& left, const A& right)
{
if (left.firstField < right.firstField) return true;
if (right.firstField < left.firstField) return false; // this case is missing
if (left.secondField < right.secondField) return true;
if (right.secondField < left.secondField) return false; // this case is missing
....
return false;
}
You are missing cases when you can conclude, that for sure, left object is "greater" than right object.

Why does defining a struct with an operand< function that isn't const break things if you use it as a key in a Map?

I use a map and within that map, the type of the key is Coordinates:
struct Coordinates
{
int x;
int y;
Coordinates()
{
x = -1;
y = -1;
};
Coordinates(int a, int b) : x(a), y(b) {};
bool operator<(const Coordinates& otherCords) const
{
int thisSize;
int otherSize;
if (x >= y)
{
thisSize = x - y;
}
else
{
thisSize = y - x;
}
if (otherCords.x >= otherCords.y)
{
otherSize = otherCords.x - otherCords.y;
}
else
{
otherSize = otherCords.y - otherCords.x;
}
return thisSize < otherSize;
}
};
Took my quite a while to realize my operand function wasn't being detected by the map because it wasn't const. Why is that so?
Short answer: because that's the requirement of the map class.
Longer answer: The keys of a map are const and cannot be modified (because this could break the sort order the map relies on). Since the keys are constant values, any comparison function used with them needs to be const.
It must be const since you're not allowed to change the value of the key while it's in the map. In order to change a key, you must extract the element and reinsert it with a new key.
A bigger problem is that your operator< does not fulfill the strict weak ordering requirement - and many coordinates are likely to get rejected when you try to enter them into the map because an equal coordinate already exists. According to your function:
{0,0} == {1,1} == {2,2} == {3,3} // all where std::abs(x-y) == 0 are equal
{0,1} == {1,0} == {1,2} == {2,1} // all where std::abs(x-y) == 1 are equal
{0,2} == {2,0} == {1,3} == {3,1} // all where std::abs(x-y) == 2 are equal
...and so on...
One remedy could be to change the comparison function to:
bool operator<(const Coordinates& otherCords) const {
if(x==otherCords.x) return y < otherCords.y;
return x < otherCords.x;
}
Or simpler:
#include <tuple>
...
bool operator<(const Coordinates& otherCords) const {
return std::tie(x,y) < std::tie(otherCords.x, otherCords.y);
}

C++ std::set<string> Alphanumeric custom comparator

I'm solving a problem with a sorting non-redundant permutation of String Array.
For example, if input string is "8aC", then output should be order like {"Ca8","C8a", "aC8", "a8C", "8Ca", "9aC"}.I chose C++ data structure set because each time I insert the String into std:set, set is automatically sorted and eliminating redundancy. The output is fine.
But I WANT TO SORT SET IN DIFFERENT ALPHANUMERIC ORDER which is different from default alphanumeric sorting order. I want to customize the comparator of set the order priority like: upper case> lower case > digit.
I tried to customize comparator but it was quite frustrating. How can I customize the sorting order of the set? Here's my code.
set<string, StringCompare> setl;
for (i = 0; i < f; i++)
{
setl.insert(p[i]); //p is String Array. it has the information of permutation of String.
}
for (set<string>::iterator iter = setl.begin(); iter != setl.end(); ++iter)
cout << *iter << endl; //printing set items. it works fine.
struct StringCompare
{
bool operator () (const std::string s_left, const std::string s_right)
{
/*I want to use my character comparison function in here, but have no idea about that.
I'm not sure about that this is the right way to customize comparator either.*/
}
};
int compare_char(const char x, const char y)
{
if (char_type(x) == char_type(y))
{
return ( (int) x < (int) y) ? 1 : 0 ;
}
else return (char_type(x) > char_type(y)) ? 1 : 0;
}
int char_type(const char x)
{
int ascii = (int)x;
if (ascii >= 48 && ascii <= 57) // digit
{
return 1;
}
else if (ascii >= 97 && ascii <= 122) // lowercase
{
return 2;
}
else if (ascii >= 48 && ascii <= 57) // uppercase
{
return 3;
}
else
{
return 0;
}
}
You are almost there, but you should compare your string lexicographically.
I roughly added small changes to your code.
int char_type( const char x )
{
if ( isupper( x ) )
{
// upper case has the highest priority
return 0;
}
if ( islower( x ) )
{
return 1;
}
if ( isdigit( x ) )
{
// digit has the lowest priority
return 2;
}
// something else
return 3;
}
bool compare_char( const char x, const char y )
{
if ( char_type( x ) == char_type( y ) )
{
// same type so that we are going to compare characters
return ( x < y );
}
else
{
// different types
return char_type( x ) < char_type( y );
}
}
struct StringCompare
{
bool operator () ( const std::string& s_left, const std::string& s_right )
{
std::string::const_iterator iteLeft = s_left.begin();
std::string::const_iterator iteRight = s_right.begin();
// we are going to compare each character in strings
while ( iteLeft != s_left.end() && iteRight != s_right.end() )
{
if ( compare_char( *iteLeft, *iteRight ) )
{
return true;
}
if ( compare_char( *iteRight, *iteLeft ) )
{
return false;
}
++iteLeft;
++iteRight;
}
// either of strings reached the end.
if ( s_left.length() < s_right.length() )
{
return true;
}
// otherwise.
return false;
}
};
Your comparator is right. I would turn parameters to const ref like this
bool operator () (const std::string &s_left, const std::string &s_right)
and start by this simple implementation:
return s_left < s_right
This will give the default behaviour and give you confidence you are on the right track.
Then start comparing one char at the time with a for loop over the shorter between the length of the two strings. You can get chars out the string simply with the operator[] (e.g. s_left[i])
You're very nearly there with what you have.
In your comparison functor you are given two std::strings. What you need to do is to find the first position where the two strings differ. For that, you can use std::mismatch from the standard library. This returns a std::pair filled with iterators pointing to the first two elements that are different:
auto iterators = std::mismatch(std::begin(s_left), std::end(s_left),
std::begin(s_right), std::end(s_right));
Now, you can dereference the two iterators we've been given to get the characters:
char c_left = *iterators.first;
char c_right = *iterators.second;
You can pass those two characters to your compare_char function and it should all work :-)
Not absoloutely sure about this, but you may be able to use an enumerated class towards your advantage or an array and choose to read from certain indices in which ever order you like.
You can use one enumerated class to define the order you would like to output data in and another that contains the data to be outputed, then you can set a loop that keeps on looping to assign the value to the output in a permuted way!
namespace CustomeType
{
enum Outs { Ca8= 0,C8a, aC8, a8C, 8Ca, 9aC };
enum Order{1 = 0 , 2, 3 , 4 , 5};
void PlayCard(Outs input)
{
if (input == Ca8) // Enumerator is visible without qualification
{
string[] permuted;
permuted[0] = Outs[0];
permuted[1] = Outs[1];
permuted[2] = Outs[2];
permuted[3] = Outs[3];
permuted[4] = Outs[4];
}// else use a different order
else if (input == Ca8) // this might be much better
{
string[] permuted;
for(int i = 0; i<LessThanOutputLength; i++)
{
//use order 1 to assign values from Outs
}
}
}
}
This should work :
bool operator () (const std::string s_left, const std::string s_right)
{
for(int i = 0;i < s_left.size();i++){
if(isupper(s_left[i])){
if(isupper(s_right[i])) return s_left[i] < s_right[i];
else if(islower(s_right[i]) || isdigit(s_right[i]))return true;
}
else if(islower(s_left[i])){
if(islower(s_right[i])) return s_left[i] < s_right[i];
else if(isdigit(s_right[i])) return true;
else if(isupper(s_right[i])) return false;
}
else if(isdigit(s_left[i])){
if(isdigit(s_right[i])) return s_left[i] < s_right[i];
else if(islower(s_right[i]) || isupper(s_right[i])) return false;
}
}
}

Checking union equality

struct Something {
union {
float k;
int n;
};
bool isFloat;
bool operator==(const Something& mS)
{
if(isFloat != mS.isFloat) return false;
if(isFloat && mS.k == k) return true;
if(!isFloat && mS.n == n) return true;
}
};
My implementation of Something::operator== seems rather expensive and convoluted. Is this the only way to check equality in classes with union types?
Or is there a better way that avoids branches/checking additional variables?
bool operator==(const Something& mS)
{
if (isFloat != mS.isFloat)
{
return false;
}
else if (isFloat)
{
return mS.k == k;
}
else
{
return mS.n == n;
}
}
Clear and debuggable with the minimum number of checks. You want to have a constructor and/or set methods to ensure isFloat is correct at all times.
You can remove one redundant check, and perhaps enhance readability slightly, by replacing the last two lines with
if(isFloat != mS.isFloat) return false; // As you have
return isFloat ? mS.k == k : mS.n == n;
(or the equivalent if construct, as in Sean Perry's answer) but the compiler will probably do just as good a job of optimising your version.
There's no way to avoid a runtime check that the types match. You might consider a ready-made discriminated union type like Boost.Variant; it won't be any more efficient, but it might be easier and less error-prone to use.
return (isFloat && mS.isFloat && k==mS.k) || (!isFloat && !mS.isFloat && n==mS.n);
I do not think that you can escape checking all the conditions. So the question can be how to write them more simpler and expressively.
I would write them the following way
bool operator==( const Something &mS ) const
{
return ( ( isFloat == mS.isFloat ) && ( isFloat ? k == mS.k : n == mS.n ) );
}

What is the best way to implement operator<?

Sorry if this is a stupid question, but it's something that I'm curious about.
I am overloading the less-than operator for my sort algorithm based on last name, first name, middle name. I realize there is not a right or wrong here, but I'm curious as to which style is written better or preferred among fellow programmers.
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last < key.m_Last)
|| ( (m_Last == key.m_Last) && (m_First < key.m_First) )
|| ( (m_Last == key.m_Last) && (m_First == key.m_First) && (m_Middle < key.m_Middle) )
return true;
return false;
}
or
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last < key.m_Last)
return true;
else if ( (m_Last == key.m_Last) && (m_First < key.m_First) )
return true;
else if ( (m_Last == key.m_Last) && (m_First == key.m_First) && (m_Middle < key.m_Middle) )
return true;
else
return false;
}
or
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last < key.m_Last)
return true;
if (m_Last == key.m_Last)
if (m_First < key.m_First)
return true;
if (m_Last == key.m_Last)
if (m_First == key.m_First)
if (m_Middle < key.m_Middle)
return true;
return false;
}
I prefer:
bool CPerson::operator<(const CPerson& key) const
{
if (m_Last == key.m_Last) {
if (m_First == key.m_First) {
return m_Middle < key.m_Middle;
}
return m_First < key.m_First;
}
return m_Last < key.mLast;
}
Nice and systematic, and it is obvious how new members can be added.
Because these are strings, the repeated comparison may be needlessly inefficient. Following David Hamman's suggestion, here is a version which only does the comparisons once per string (at most):
bool CPerson::operator<(const CPerson& key) const
{
int last(m_Last.compare(key.m_Last));
if (last == 0) {
int first(m_First.compare(key.m_First));
if (first == 0) {
return m_Middle < key.m_Middle;
}
return first < 0;
}
return last < 0;
}
All of your implementations are essentially the same and they are all wrong by any reasonable definition of sort order for people's names. Your algorithm will place Jonathan Abbott Zyzzyk ahead of Jonathan Zuriel Aaron.
What you want is person A's name is less than person B's name if:
The last name of person A is less than the last name of person B or
The two have the same last name and
The first name of person A is less than the first name of person B or
The two have the same first name and the middle name of person A is less than the middle name of person B.
Whether you implement this as a single boolean expression versus a staged if/else sequence is a bit of personal preference. My preference is the single boolean expression; to me that logical expression is clearer than a cluttered if/else sequence. But apparently I'm weird. Most people prefer the if/else construct.
Edit, per request
As a single boolean expression,
bool Person::operator< (const Person& other) const {
return (last_name < other.last_name) ||
((last_name == other.last_name) &&
((first_name < other.first_name) ||
((first_name == other.first_name) &&
(middle_name < other.middle_name))));
}
I find the first one the most difficult to read of the three (although none of them are too difficult) and the first one has unnecessary parentheses. The second one is my personal preference, because the third one seems too long and verbose.
This really is subjective though.
I normally write a comparison function roughly like this:
bool whatever::operator<(whatever const &other) {
if (key1 < other.key1)
return true;
if (other.key1 < key1)
return false;
// compare the second key item because the first ones were equal.
if (key2 < other.key2)
return true;
if (other.key2 < key2)
return false;
// repeat for as many keys as needed
// for the last key item, we can skip the second comparison:
if (keyN < other.keyN)
return true;
return false; // other.keyN >= keyN.
}
Along a slightly different vein, all of the solutions (including my first answer) tend to compare names twice, once for less than and again for equality. Since sort is at best an N*logN algorithm, efficiency can be quite important when sorting a big list of names, and these duplicative comparisons are rather inefficient. The string::compare method provides a mechanism for bypassing this problem:
bool Person::operator< (const Person& other) const {
int cmp = last_name.compare (other.last_name);
if (cmp < 0) {
return true;
} else if (cmp == 0) {
cmp = first_name.compare (other.first_name);
if (cmp < 0) {
return true;
} else if (cmp == 0) {
cmp = middle_name.compare (other.middle_name);
if (cmp < 0) {
return true;
}
}
}
return false;
}
Edit, per request
Elided.
A boolean version of the above will either result in undefined behavior or will use multiple embedded uses of the ternary operator. It is ugly even given my penchant for hairy boolean expressions. Sorry, Mankarse.
I like to reduce this to tuples, which already implement this kind of lexicographical ordering. For example, if you have boost, you can write:
bool Person::operator< (const Person& Rhs) const
{
return boost::tie(m_Last, m_First, m_Middle) < boost::tie(Rhs.m_Last, Rhs.m_First, Rhs.m_Middle);
}