So maps need you to implement the operator < if you want to use custom objects as keys.
struct example{
example( int id, String stuff);
int id;
String stuff;
bool operator<( const example& rhs ) const;
}
bool example::operator<( const example& rhs ) const
{
if( id< rhs.id ) return true;
if( rhs.id< id) return false;
if( stuff< rhs.stuff) return true;
if( rhs.stuff< stuff) return false;
return false;
}
From the examples I have seen (see discussion: Why std::map overloaded operator < does not use the Compare ) just return true if the lhs < rhs...
Why does map require you to implement the < operator? Is it because maps are doing binary search in the background to find if the key matched?
Even if it is doing binary search it will still have to compare if the objects are equal at the end right?
example example1 = example(1,"a");
example example2 = example(2,"b");
map<example, String> mymap;
mymap.insert({example1,"hello"});
mymap.insert({example2,"world"});
cout << mymap[example(1,"a")] << endl;
It never asks for an implementation of the operator= so how does it know the new object I have created is the same as the first entry of the map. Would it print "hello"?
It stores values in a balanced binary tree, red black tree as usual, so comparison operator is required for this tree
also keep in mind that if a < b == false and b < a == false it is considered that a is equal to b
I know that I have to overload operator < for std::set.
I overload operator < with two classes: "UniqueID" and "UniqueIDWithBug".
The only difference is "UniqueID" added code this->unique_id_a_ == t.unique_id_a_ while comparing.
Then I put same elements into the two sets.
Finally I find one element inside the sets.
One set can find it, another can not.
This problem confused me for a long time.
struct UniqueID {
uint64_t unique_id_a_{0};
uint64_t unique_id_b_{0};
bool operator<(const UniqueID &t) const {
if (this->unique_id_a_ < t.unique_id_a_) {
return true;
}
if (this->unique_id_a_ == t.unique_id_a_ &&
this->unique_id_b_ < t.unique_id_b_) {
return true;
}
return false;
}
};
struct UniqueIDWithBug {
uint64_t unique_id_a_{0};
uint64_t unique_id_b_{0};
bool operator<(const UniqueIDWithBug &t) const {
if (this->unique_id_a_ < t.unique_id_a_) {
return true;
}
return (this->unique_id_b_ < t.unique_id_b_);
}
};
// init data
std::set<UniqueID> _set = {
{17303934402126834534u, 2922971136},
{8520106912500150839u, 3118989312},
{9527597377742531532u, 2171470080},
{10912468396223017462u, 3972792320},
};
std::set<UniqueIDWithBug> _set_with_bug = {
{17303934402126834534u, 2922971136},
{8520106912500150839u, 3118989312},
{9527597377742531532u, 2171470080},
{10912468396223017462u, 3972792320}};
UniqueID _unique_id = {10912468396223017462u, 3972792320};
UniqueIDWithBug _unique_id_with_bug = {10912468396223017462u, 3972792320};
if (_set.find(_unique_id) == _set.end()) {
std::cout << "_set not find" << std::endl;
}
if (_set_with_bug.find(_unique_id_with_bug) == _set_with_bug.end()) {
std::cout << "_set_with_bug not find" << std::endl;
}
The outputs:
_set_with_bug not find
The less-than operation you define for use with std::set (and others) must be a valid strict weak ordering.
Your UniqueIDWithBug ordering is not.
For example, consider:
UniqueIDWithBug a{1, 10};
UniqueIDWithBug b{2, 5};
Now observe that both a < b and b < a are true. This is just a quick demonstration that you do not have a strict weak ordering; indeed, this is not an ordering at all!
So your program has undefined behaviour. The internals of the std::set mechanism assume a valid ordering, but yours is not. In this case the observable result was "element not found". It could have been "make a pizza".
Constructing a good strict weak ordering can be difficult, but you've already done the hard work, because UniqueID's ordering is correct.
Alternatively, abandon the ordering entirely, define a hash function, and switch to unordered_set.
In my code I have those lines:
if(mymap.count(plan) == 0) {
std::vector<XYZ> v;
v.reserve(10);
mymap.emplace(plan, v);
std::cout << "Plan " << plan.normal << " # " << plan.O << " added";
}
//I inserted this code for debugging
std::map<Plan, std::vector<XYZ>>::const_iterator it = mymap.find(plan);
if(it == this->intersections.end())
std::cout << "not found";
How is it possible that I can read in the console plan added and just after not found ?
My map is declared as such:
std::map<Plan, std::vector<XYZ>, PlanComp> mymap;
At some point I thougt it comes from the comparator, but it respects irreflexivity, antisymmetry, transitivity, transitivity of equivalence (which is enough according to this blog) :
struct PlanComp {
bool operator()(const Plan& l, const Plan& n) const {
return (l.O.x != n.O.x) || (l.O.y != n.O.y) || (l.O.z != n.O.z)
|| (l.normal.x != n.normal.x) || (l.normal.y != n.normal.y) || (l.normal.z != n.normal.z);
}
};
struct XYZ {
double x;
double y;
double z;
};
struct Plan {
XYZ O;
XYZ plan;
};
Your comparator does not define a strict weak ordering (loosely speaking, "less than" semantics which define an order for your elements). Therefore your code exhibits undefined behaviour.
The simplest solution would be to use a lexicographical comparator - compare x first, then compare y only in the event of a tie, and so on. In C++11 that's even simpler; operator < for tuples already does this for you (and you can use std::tie to get the tuples). See the answers to Operator < and strict weak ordering for examples.
I want to sort suffices of a string.
The most simple way to do that is putting all the suffices into map.
In order to use memory efficiently, I pass suffix as (str+i), where str is char* and i is a position suffix starts with. However, I found out that map is not going to sort these suffices. Here goes an example
typedef std::map < char*, int,Comparator> MapType;
MapType data;
// let's declare some initial values to this map
char* bob=(char* )"Bobs score";
char* marty=(char* ) "Martys score";
data.insert(pair<char*,int>(marty+1,15));
data.insert(pair<char*,int>(bob+1,10));
MapType::iterator end = data.end();
for (MapType::iterator it = data.begin(); it != end; ++it) {
std::cout << "Who(key = first): " << it->first;
std::cout << " Score(value = second): " << it->second << '\n';
}
The output is
Who(key = first): obs score Score(value = second): 10
Who(key = first): artys score Score(value = second): 15
However, strcmp, standard function for comparing strings, works correctly for bob+1 and marty+1. It says marty+1 is less than bob+1.
The map will sort by the address of the char*, not lexiographically. Change the key to a std::string or define a comparator.
EDIT:
It looks as though you have attempted to define a Comparator but the definition of it is not posted. Here is an example:
#include <iostream>
#include <map>
#include <string.h>
struct cstring_compare
{
bool operator()(const char* a_1, const char* a_2) const
{
return strcmp(a_1, a_2) < 0;
}
};
typedef std::map<const char*, int, cstring_compare> cstring_map;
int main()
{
cstring_map m;
m["bcd"] = 1;
m["acd"] = 1;
m["abc"] = 1;
for (cstring_map::iterator i = m.begin(); i != m.end(); i++)
{
std::cout << i->first << "\n";
}
return 0;
}
Output:
abc
acd
bcd
define a custom Comparator, eg
class compare_char {
public:
bool operator()(const char* lhs, const char* rhs) { return strcmp(lhs, rhs); }
};
define your map using this comparator instead of whatever you currently have. Alternatively, use a map with a key type that has a comparison operator that works with values, a std::string is better for you. Currently you have a map using char* as the key which compares char* types, ie. the value of the pointer, not the contents.
You should add the comparer class or function you are using since that is where your error is probably coming from.
There is a slight difference between strcmp and a map comparaison function.
strcmp returns 0 if a == b, -1 if a < b, 1 if a > b
comp returns true is a < b, false otherwise.
A correct way to implement the comparison function is the following:
bool operator() (char* lhs, char* rhs) const
{
return strcmp(lhs,rhs) < 0;
}
I'm trying to check if a given key is in a map and somewhat can't do it:
typedef map<string,string>::iterator mi;
map<string, string> m;
m.insert(make_pair("f","++--"));
pair<mi,mi> p = m.equal_range("f");//I'm not sure if equal_range does what I want
cout << p.first;//I'm getting error here
so how can I print what is in p?
Use map::find and map::end:
if (m.find("f") == m.end()) {
// not found
} else {
// found
}
To check if a particular key in the map exists, use the count member function in one of the following ways:
m.count(key) > 0
m.count(key) == 1
m.count(key) != 0
The documentation for map::find says: "Another member function, map::count, can be used to just check whether a particular key exists."
The documentation for map::count says: "Because all elements in a map container are unique, the function can only return 1 (if the element is found) or zero (otherwise)."
To retrieve a value from the map via a key that you know to exist, use map::at:
value = m.at(key)
Unlike map::operator[], map::at will not create a new key in the map if the specified key does not exist.
C++20 gives us std::map::contains to do that.
#include <iostream>
#include <string>
#include <map>
int main()
{
std::map<int, std::string> example = {{1, "One"}, {2, "Two"},
{3, "Three"}, {42, "Don\'t Panic!!!"}};
if(example.contains(42)) {
std::cout << "Found\n";
} else {
std::cout << "Not found\n";
}
}
You can use .find():
map<string,string>::iterator i = m.find("f");
if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }
C++17 simplified this a bit more with an If statement with initializer.
This way you can have your cake and eat it too.
if ( auto it{ m.find( "key" ) }; it != std::end( m ) )
{
// Use `structured binding` to get the key
// and value.
const auto&[ key, value ] { *it };
// Grab either the key or value stored in the pair.
// The key is stored in the 'first' variable and
// the 'value' is stored in the second.
const auto& mkey{ it->first };
const auto& mvalue{ it->second };
// That or just grab the entire pair pointed
// to by the iterator.
const auto& pair{ *it };
}
else
{
// Key was not found..
}
m.find == m.end() // not found
If you want to use other API, then find go for m.count(c)>0
if (m.count("f")>0)
cout << " is an element of m.\n";
else
cout << " is not an element of m.\n";
I think you want map::find. If m.find("f") is equal to m.end(), then the key was not found. Otherwise, find returns an iterator pointing at the element found.
The error is because p.first is an iterator, which doesn't work for stream insertion. Change your last line to cout << (p.first)->first;. p is a pair of iterators, p.first is an iterator, p.first->first is the key string.
A map can only ever have one element for a given key, so equal_range isn't very useful. It's defined for map, because it's defined for all associative containers, but it's a lot more interesting for multimap.
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
return (container.find(key) != std::end(container));
}
Of course if you wanted to get fancier you could always template out a function that also took a found function and a not found function, something like this:
template <typename T, typename Key, typename FoundFunction, typename NotFoundFunction>
void find_and_execute(const T& container, const Key& key, FoundFunction found_function, NotFoundFunction not_found_function)
{
auto& it = container.find(key);
if (it != std::end(container))
{
found_function(key, it->second);
}
else
{
not_found_function(key);
}
}
And use it like this:
std::map<int, int> some_map;
find_and_execute(some_map, 1,
[](int key, int value){ std::cout << "key " << key << " found, value: " << value << std::endl; },
[](int key){ std::cout << "key " << key << " not found" << std::endl; });
The downside to this is coming up with a good name, "find_and_execute" is awkward and I can't come up with anything better off the top of my head...
map<string, string> m;
check key exist or not, and return number of occurs(0/1 in map):
int num = m.count("f");
if (num>0) {
//found
} else {
// not found
}
check key exist or not, and return iterator:
map<string,string>::iterator mi = m.find("f");
if(mi != m.end()) {
//found
//do something to mi.
} else {
// not found
}
in your question, the error caused by bad operator<< overload, because p.first is map<string, string>, you can not print it out. try this:
if(p.first != p.second) {
cout << p.first->first << " " << p.first->second << endl;
}
Be careful in comparing the find result with the the end like for map 'm' as all answer have
done above
map::iterator i = m.find("f");
if (i == m.end())
{
}
else
{
}
you should not try and perform any operation such as printing the key or value with iterator i if its equal to m.end() else it will lead to segmentation fault.
Comparing the code of std::map::find and std::map::count, I'd say the first may yield some performance advantage:
const_iterator find(const key_type& _Keyval) const
{ // find an element in nonmutable sequence that matches _Keyval
const_iterator _Where = lower_bound(_Keyval); // Here one looks only for lower bound
return (_Where == end()
|| _DEBUG_LT_PRED(this->_Getcomp(),
_Keyval, this->_Key(_Where._Mynode()))
? end() : _Where);
}
size_type count(const key_type& _Keyval) const
{ // count all elements that match _Keyval
_Paircc _Ans = equal_range(_Keyval); // Here both lower and upper bounds are to be found, which is presumably slower.
size_type _Num = 0;
_Distance(_Ans.first, _Ans.second, _Num);
return (_Num);
}
I know this question already has some good answers but I think my solution is worth of sharing.
It works for both std::map and std::vector<std::pair<T, U>> and is available from C++11.
template <typename ForwardIterator, typename Key>
bool contains_key(ForwardIterator first, ForwardIterator last, Key const key) {
using ValueType = typename std::iterator_traits<ForwardIterator>::value_type;
auto search_result = std::find_if(
first, last,
[&key](ValueType const& item) {
return item.first == key;
}
);
if (search_result == last) {
return false;
} else {
return true;
}
}
map <int , char>::iterator itr;
for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
{
if (itr->second == 'c')
{
cout<<itr->first<<endl;
}
}
If you want to compare pair of map you can use this method:
typedef map<double, double> TestMap;
TestMap testMap;
pair<map<double,double>::iterator,bool> controlMapValues;
controlMapValues= testMap.insert(std::pair<double,double>(x,y));
if (controlMapValues.second == false )
{
TestMap::iterator it;
it = testMap.find(x);
if (it->second == y)
{
cout<<"Given value is already exist in Map"<<endl;
}
}
This is a useful technique.