QMap doesn't insert when < operator compares two identical objects - c++

I'm trying to learn Qt. I experienced some issues but generally I find the solution by googling. But this afternoon I had an issue with QMap and I don't understand the problem.
I've created a class File and I overrided the operator< in order to be able to use it as key in QMap<File, bool>. The issue is that when I try to initialize a QMap by inserting entries the file map doesn't contain a duplicate entry in the sense of the implementation of the operator<.
bool File::operator<(const File &file) const{
if(comparator == NAME){
if(this->getFileName() != file.getFileName()){
return this->getFileName() < file.getFileName();
}
return false;
}
return this->getFileHash() < file.getFileHash();
}
QMap initialization:
for(File file: files){
//filesCheckStatus edclared in the header file QMap<File, bool> filesCheckStatus;
filesCheckStatus.insert(file, false);
}
In this example when comparator NAME is used entries with the same name (QString) are inserted only once.
In case I return false in all cases the final map contains only one entry (the first inserted).
Could someone explain this behavior?

In this example when comparator NAME is used entries with the same
name (QString) are inserted only once.
That's how maps and sets work. Each key is unique[1] in the collection. If you want multiple Files that compare the same, you could use QMultiMap<File, bool>, or QVector<std::pair<File, bool>>.
In case I return false in all cases the final map contains only one
entry (the first inserted).
That's because the ordering that defines compares everything as equivalent to everything else.
Unique under the equivalence relation !(a < b) && !(b < a). More generally for a binary predicate comp, you have a binary predicate equiv that is defined by equiv(a, b) = !comp(a, b) && !comp(b, a)

Related

Key already exists in unordered_map, but "find" returns as not found

I constructed an unordered_map using key type rot3d, which is defined below:
#ifndef EPS6
#define EPS6 1.0e-6
#endif
struct rot3d
{
double agl[3]; // alpha, beta, gamma in ascending order
bool operator==(const rot3d &other) const
{
// printf("== used\n");
return abs(agl[0]-other.agl[0]) <= EPS6 && abs(agl[1]-other.agl[1]) <= EPS6 && abs(agl[2]-other.agl[2]) <= EPS6;
}
};
Equality of rot3d is defined by the condition that each component is within a small range of the same component from the other rot3d object.
Then I defined a value type RotMat:
struct RotMat // rotation matrix described by a pointer to matrix and trunction number
{
cuDoubleComplex *mat = NULL;
int p = 0;
};
In the end, I defined a hash table from rot3d to RotMat using self-defined hash function:
struct rot3dHasher
{
std::size_t operator()(const rot3d& key) const
{
using std::hash;
return (hash<double>()(key.agl[0]) ^ (hash<double>()(key.agl[1]) << 1) >> 1) ^ (hash<double>()(key.agl[2]) << 1);
}
};
typedef std::unordered_map<rot3d,RotMat,rot3dHasher> HashRot2Mat;
The problem I met was, a key was printed to be in the hash table, but the function "find" didn't find it. For instance, I printed a key using an iterator of the hash table:
Key: (3.1415926535897931,2.8198420991931510,0.0000000000000000)
But then I also got this information indicating that the key was not found:
(3.1415926535897931,2.8198420991931505,0.0000000000000000) not found in the hash table.
Although the two keys are not 100% the same, the definition of "==" should ensure them to be equal. So why am I seeing this key in the hash table, but it was not found by "find"?
Hash-based equivalence comparisons are allowed to have false positives, which are resolved by calling operator==.
Hash-based equivalence comparisons are not allowed to have false negatives, but yours does. Your two "not 100% the same" keys have different hash values, so the element is not even found as a candidate for testing using operator==.
It is necessary that (a == b) implies (hash(a) == hash(b)) and your definitions break this precondition. A hashtable with a broken precondition can misbehave in many ways, including not finding the item you are looking for.
Use a different data structure that is not dependent on hashing, but nearest-neighbor matching. An octtree would be a smart choice.
Equality of rot3d is defined by the condition that each component is within a small range of the same component from the other rot3d object.
This is not an equivalence. You must have that a==b and b==c implies a==c. Yours fails this requirement.
Using a non-equality in a std algorithm or container breaks the std preconditions, which means your program is ill-formed, no diagnostic required.
Also your hash hashes equivalent values differently. Also illegal.
One way to fix this is to build buckets. Each bucket has a size of your epsilon.
To find if a value is in your buckets, check the bucket you'd put the probe value in, plus all adjacent buckets (3^3 or 27 of them).
For each element, double check distance.
struct bucket; // array of 3 doubles, each a multiple of EPS6. Has == and hash. Also construct-from-rod3d that rounds.
bucket get_bucket(rot3d);
Now, odds are that you are just caching. And within EPS-ish is good enough.
template<class T, class B>
struct adapt:T{
template<class...Args>
auto operator()(Args&&...args)const{
return T::operator()( static_cast<B>(std::forward<Args>(args))... );
}
using is_transparent=void;
};
std::unordered_map<bucket, RotMat, adapt<std::hash<rot3d>, bucket>, adapt<std::equal_to<>, bucket>> map;
here we convert rod3ds to buckets on the fly.

custom comparison method for c++ map based on key and value

I searched a lot but I didnt find the answer I want.
I need to sort c++ map based on keys and values.
something like this method :
bool mycomp(mii::iterator a, mii::iterator b) {
if (a->second > b->second)
return true;
else if (a->second < b->second)
return false;
else
return a->first > a->second;
}
and use it something like that
sort(m.begin() , m.end(), mycomp);
where m is :
map<int,int> m;
can I do such thing ? if yes, what should be the correct syntax.
When you define the std::map you can provide a Compare function as a template parameter. See the reference for more details.
However, I believe this is only for key sorting. Key-value sorting is not inherit to the map structure (i.e. what happens if the value changes?)

How does std::set comparator function work?

Currently working on an algorithm problems using set.
set<string> mySet;
mySet.insert("(())()");
mySet.insert("()()()");
//print mySet:
(())()
()()()
Ok great, as expected.
However if I put a comp function that sorts the set by its length, I only get 1 result back.
struct size_comp
{
bool operator()(const string& a, const string& b) const{
return a.size()>b.size();
}
};
set<string, size_comp> mySet;
mySet.insert("(())()");
mySet.insert("()()()");
//print myset
(())()
Can someone explain to me why?
I tried using a multi set, but its appending duplicates.
multiset<string,size_comp> mSet;
mSet.insert("(())()");
mSet.insert("()()()");
mSet.insert("()()()");
//print mset
"(())()","()()()","()()()"
std::set stores unique values only. Two values a,b are considered equivalent if and only if
!comp(a,b) && !comp(b,a)
or in everyday language, if a is not smaller than b and b is not smaller than a. In particular, only this criterion is used to check for equality, the normal operator== is not considered at all.
So with your comparator, the set can only contain one string of length n for every n.
If you want to allow multiple values that are equivalent under your comparison, use std::multiset. This will of course also allow exact duplicates, again, under your comparator, "asdf" is just as equivalent to "aaaa" as it is to "asdf".
If that does not make sense for your problem, you need to come up with either a different comparator that induces a proper notion of equality or use another data structure.
A quick fix to get the behavior you probably want (correct me if I'm wrong) would be introducing a secondary comparison criterion like the normal operator>. That way, we sort by length first, but are still able to distinguish between different strings of the same length.
struct size_comp
{
bool operator()(const string& a, const string& b) const{
if (a.size() != b.size())
return a.size() > b.size();
return a > b;
}
};
The comparator template argument, which defaults to std::less<T>, must represent a strict weak ordering relation between values in its domain.
This kind of relation has some requirements:
it's not reflexive (x < x yields false)
it's asymmetric (x < y implies that y < x is false)
it's transitive (x < y && y < z implies x < z)
Taking this further we can define equivalence between values in term of this relation, because if !(x < y) && !(y < x) then it must hold that x == y.
In your situation you have that ∀ x, y such that x.size() == y.size(), then both comp(x,y) == false && comp(y,x) == false, so since no x or y is lesser than the other, then they must be equal.
This equivalence is used to determine if two items correspond to the same, thus ignoring second insertion in your example.
To fix this you must make sure that your comparator never returns false for both comp(x,y) and comp(y,x) if you don't want to consider x equal to y, for example by doing
auto cmp = [](const string& a, const string& b) {
if (a.size() != b.size())
return a.size() > b.size();
else
return std::less()(a, b);
}
So that for input of same length you fallback to normal lexicographic order.
This is because equality of elements is defined by the comparator. An element is considered equal to another if and only if !comp(a, b) && !comp(b, a).
Since the length of "(())()" is not greater, nor lesser than the length of "()()()", they are considered equal by your comparator. There can be only unique elements in a std::set, and an equivalent object will overwrite the existing one.
The default comparator uses operator<, which in the case of strings, performs lexicographical ordering.
I tried using a multi set, but its appending duplicates.
Multiset indeed does allow duplicates. Therefore both strings will be contained despite having the same length.
size_comp considers only the length of the strings. The default comparison operator uses lexicographic comparison, which distinguishes based on the content of the string as well as the length.

implement linear probing in c++ for a generic type

I wanted to implement linear probing for hashtabe in c++,but the key,value
pair would be of generic type like: vector< pair< key,value> >(where key,value is of generic type).
Now,in linear probing if a cell is occupied we traverse the vector until we find an empty cell and then place the new pair in that cell.
The problem is,that in generic type,how would I be able to check if a particular cell is occupied or not?
I cannot use these conditions:
if(key == '\0')//As key is not of type string
OR
if(key == 0)//As key is not of type int
So,how will be able to check if a paricular cell in the vector is empty or not?
Please correct me if I misunderstood the concept.
I think you can just check if the element of the vector has meaningful key and value:
if(vector[i] == std::pair<key, value>())
//empty
or, if you only care about the keys
if(vector[i].first == key())
//empty
This approach assumes that the default constructor of key type constructs an object that would be considered an "empty" or "invalid" key.
You could use a free function isEmpty for checking if a key type is empty. Define a templated default function that works for most types, and make special functions that cannot be handled by the default.
E.g.
template<typename T>
bool isEmpty(const T &t) {
return !t;
}
bool isEmpty(const std::string &s) {
return s.length() == 0;
}
bool isEmpty(double d) {
return d < 0;
}
isEmpty(0); // true
isEmpty(1); // false
isEmpty(std::string()); // true
isEmpty(std::string("not empty")); // false
isEmpty(1.0); // false
isEmpty(-1.0); // true
You only need to specialize for key types that do not have an operator ! or where a different logic is needed for the check
In case you don't want to rule out the possibility to have "default constructed" elements in your hash you can build a parallel data structure, like a std::bitset (if you know in advance the maximum size of your data structure) or a std::vector<bool>, that in the following i will call has. has[i] will be true if vector[i] contains a valid, actually inserted element.
So the operations should be modified as follows:
insert: go on scanning the vector until you find a position i such that has[i] == false
remove element in position i: set has[i] to false
Hope this helps

multimap with custom keys - comparison function

bool operator<(const Binding& b1, const Binding& b2)
{
if(b1.r != b2.r && b1.t1 != b2.t1)
{
if(b1.r != b2.r)
return b1.r < b2.r;
return b1.t1 < b2.t1;
}
return false;
}
I have a comparison function like above. Basically, I need to deem the objects equal if one of their attribute matches. I am using this comparison function for my multimap whose key is 'Binding' object.
The problem I face is that lower_bound and upper_bound functions return the same iterator which points to a valid object. For example (t1 = 1, r = 2) is already in the map and when I try to search it in the map with (t1 = 1, r = 2), I get a same iterator as return value of upper_bound and lower_bound functions.
Is anything wrong with the comparison function? Is there a way to figure a function where I can still ensure that the objects are equivalent even if just one of their field matches?
Shouldn't the upper_bound iterator return the object past the
The comparator for a map or multimap is expected to express a strict weak ordering relation between the set of keys. Your requirement "two objects are equivalent if just one of their fields matches" cannot be such a relation. Take these three keys:
1: r=1, t1=10
2: r=1, t1=42
3: r=2, t1=42
clearly, keys 1 and 2 are equivalent, because they have the same r. Likewise, 2 and 3 are equivalent because of the same t1. That means, that 1 and 3 have to be equivalent as well, although they have no matching fields.
As a corollary, all possible keys have to be equivalent under these circumstances, which means you dont have any ordering at all and a multimap is not the right way to go.
For your case, Boost.MultiIndex comes to mind. You could then have two separate indices for r and t1 and do your lower_bound, upper_bound and equal_range searches over both indices separately.
Your comparision function after removing redundant code can be re-written as
bool operator<(const Binding& b1, const Binding& b2)
{
if(b1.r != b2.r && b1.t1 != b2.t1)
{
//if(b1.r != b2.r) // always true
return b1.r < b2.r;
//return b1.t1 < b2.t1; // Never reached
}
return false;
}
Or by de-morgan's law
bool operator<(const Binding& b1, const Binding& b2)
{
if(b1.r == b2.r || b1.t1 == b2.t1) return false;
else return b1.r < b2.r;
}
This does not guarantee a < c if a < b and b < c
Ex: Binding(r, t): a(3, 5), b(4, 6), c(5, 5)
If your comparision function doesn't follow above crieteria, you may get strange results. (including infinite loops in some cases if library is not robust)
Your comparison function will return false if either the rs or the ts match because of the && in the if() clause. Did you mean || ? Replacing the && with || would give you a valid comparison function which compare first by the r field then the t field.
Note that std::pair already has a comparison function that does exactly that.
Your text below your code though states:
Basically, I need to deem the objects equal if one of their attribute matches
You cannot do that as it wouldn't be transitive (thus you wouldn't have strict ordering).
The inside of your if block has another if that is certain to be true, as the && clause means both sides are true.