I want to compare an element from a multimap with a string, like:
struct value {
string res;
time_t t;
};
string result;
multimap<int, value> values
value new_value;
if((values.find(new_value.res)) != result) // code snipped
{
//... do something
}
Thanks!
You can use std::find and a lambda expression
auto it=std::find_if(values.begin(), values.end(), [&](const std::pair<int,value> &pair)
{
return pair.second.res == new_value.res
});
if (it != values.end())
{
// Found it
}
If you don't have access to C++11 then you can loop over it:
for(std::map<int,value>::iterator it=values.begin(); it!=values.end(); ++it)
{
if( (*it).second.res == new_value.res)
{
// Found it,
break;
}
}
You can't use the std::multimap::find function for this, as it only searches for keys.
Instead you have to use the more generic std::find_if function, with a custom predicate. Something like
std::find_if(std::begin(values), std::end(values),
[&new_value](const std::pair<const int, value> &p)
{
return p.second == new_value.res;
});
Related
In c++ I need to search a vector containing a pair, in reverse, by the string. I cannot use a map because the strings are not unique and order is important. I then want to return a forward iterator if the string is found or the end iterator if the string is not found.
Please see below for my current code. I have no problem when the string is found but, if the string is not found, I get a segfault in the conditional statement in main.
vector<pair<string, int>>::iterator prev_it(const string& pred,
vector<pair<string, int>> prevpreds) {
vector<pair<string, int>>::reverse_iterator rit;
for(rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit) {
if (rit->first == pred) {
return (rit+1).base();}
}
if(rit == prevpreds.rend()) {
return prevpreds.end();
}
}
and in main:
int main() {
vector<pair<string, int>> test;
for(int i = 0; i <= 5; ++i) {
pair<string, int> mypair;
mypair = make_pair("X"+to_string(i%4+1), i+1);
test.emplace_back(mypair);
}
string tpred = "X"+to_string(6);
vector<pair<string, int>>::iterator tit;
tit = prev_it(tpred, test);
if (tit != test.end()) {
cout << tit->first << " " << tit->second << endl;
}
else {cout << "This is the end." << endl;}
}
The code works if tpred is one of X1 to X4. If tpred is X6 (i.e. not an element of test) then I get a segfault. What I would like to be able to do is return the end forward iterator and then, as in main(), have a conditional based on this.
Edit: I am new to c++ (about a year). I am returning a forward iterator because I need to use the iterator later and this seems clearer (but I could be wrong). As far as I understand, a multimap allows non-unique keys but will order the unique keys. I should have been clearer and said time order was important, not key order. I prefer not to use auto while developing because I like to see what I container element/iterator I am using, but point taken.
You're using an iterator of a destructed object. Pass prevpreds by reference, so the iterator maintains valid.
vector<pair<string, int>>::const_iterator prev_it(const string& pred,
const vector<pair<string, int>> &prevpreds)
{
vector<pair<string, int>>::const_reverse_iterator rit;
for (rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit)
{
if (rit->first == pred)
{
return (rit + 1).base();
}
}
return prevpreds.end();
}
int main()
{
// ...
vector<pair<string, int>>::const_iterator tit; // <-- uses const iterator
tit = prev_it(tpred, test);
// ...
}
I have a map which contains a of vector of type Messages.
std::map<std::string, std::vector<Message>> storage;
class Message has 3 member variables.
class Message
{
private:
std::string msg;
std::string msg_type;
int priority;
}
Now i am trying to delete an object which has priority(say 3) from the map. i am using the following function for it. But it doesn't work.
void deleteByMessagePriority(int priority)
{
if (checkPriorityOfMessage(priority))
{
for (std::map<std::string, std::vector<Message>>::iterator it = storage.begin(); it != storage.end(); it++)
{
std::vector<Message> listOfMsgs = it->second;
for (std::vector<Message>::iterator vec_it = listOfMsgs.begin(); vec_it != listOfMsgs.end(); vec_it++)
//for(int index = 0;index < listOfMsgs.size();index++)
{
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
}
}
}
}
Look carefully at this:
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
You're looking at the priority of one message (the one referred to by vec_it), but then what are you deleting if it matches?
Instead of writing your own loop, I'd use erase and std::remove_if to remove all the items you care about in that vector at once.
for (auto & item : storage) {
auto &vec = item.second;
auto start_junk = std::remove_if(
vec.begin(), vec.end(),
[=](Message const &m) { return m.priority == priority; });
vec.erase(start_junk, vec.end());
}
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
pop_back() removes the last element of the vector which you don't want.You want to check erase
Also remember erase() invalidates the iterators so you need iterator to the next element after a deleted element for which we can fortunately use return value of erase
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it); //returns iterator to the element after vec_it which can also be `listOfMsgs.end()`
std::vector<Message> listOfMsgs = it->second;
.
.
.
listOfMsgs.pop_back();
You're copying the list, only to modify the copy. What you meant is:
std::vector<Message>& listOfMsgs = it->second;
Then you can proceed erasing elements. As Gaurav Sehgal says, use the return value of erase:
std::vector<Message>::iterator vec_it = listOfMsgs.begin();
while (vec_it != listOfMsgs.end())
{
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it);
}
else
{
++vec_it;
}
}
I am trying to erase an entity from a list in two cases. In the first case the following worked just fine, where I have a list of pairs from which I want to erase a certain one:
bool remove(std::list<std::pair<std::string, size_t>>& myList, std::string const& name) {
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->first == name) {
myList.erase(i);
return true;
}
}
return false;
}
Here the pair gets removed from the list as it should, but when I have a list of structs it does not work as in the following:
void remove(std::list<myStruct>& myList , const std::string& name) {
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->name == name) {
myList.erase(i);
}
}
The program crashes in the erase part. Only if I plug in myList.erase(i++) then it works. Why is this??
Have I done something foul in the first case and it just happened to work, but then in the second case it does not? I can not understand the reason.
You're working on an invalidated iterator. That's undefined behavior. That's why erase returns a valid iterator.
If you want to only erase the first matching element, use find_if and then erase if the returned iterator isn't equal to end().
auto it = find_if(myList.begin(), myList.end(), [&name](auto const& p){
return p.first == name;
});
if(it == myList.end()){
return false;
}
myList.erase(it);
return true;
Otherwise, just use erase-remove idiom and be wary for its pitfalls (erase will happily accept 1 argument, but it'll call a different overload):
auto it = remove_if(myList.begin(), myList.end(), [&name](auto const& p){
return p.first == name;
});
myList.erase(it, myList.end());
The above is a generic version (will work if you change myList's type to vector for example), but as per ksfone's reply, std::list<T> implements member function template remove_if:
myList.remove_if([&name](auto const& p){
return p.first == name;
});
Your first loop removes an entry from the list and stops.
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->first == name) {
myList.erase(i);
return true;
}
}
while your second loop continues looking for matching elements:
for (auto i = myList.begin(); i != myList.end(); i++) {
if(i->name == name) {
myList.erase(i);
}
}
When you erase i from myList, i becomes invalid - we have no idea what it references now that the element it was talking about has gone away and may have been deleted, returned to the os and it's memory used by another thread.
The very next thing you do is i++ which is undefined behavior, since i is an invalid iterator.
The erase operator returns the next valid iterator, so you could write this:
for (auto i = myList.begin(); i != myList.end();) {
if(i->name == name)
i = myList.erase(i);
else
i++;
}
Or you could write:
void remove(std::list<myStruct>& myList , const std::string& name) {
myList.remove_if([&](const myStruct& it) { return it.name == name; });
}
or if your compiler supports C++14
void remove(std::list<myStruct>& myList , const std::string& name) {
myList.remove_if([&](auto& it) { return it.name == name; });
}
As in
#include <iostream>
#include <list>
#include <string>
struct A {
std::string name_;
A(const char* name) : name_(name) {}
};
void dump(const std::list<A>& list) {
for (auto&& a : list) {
std::cout << a.name_ << ' ';
}
std::cout << '\n';
}
int main() {
std::list<A> myList { { "hello", "pizza", "world", "pizza" } };
dump(myList);
const std::string name = "pizza";
myList.remove_if([&](const A& it){ return it.name_ == name; });
dump(myList);
return 0;
}
Live demo: http://ideone.com/SaWejv
erase() invalidates the erased iterator. The for loop then attempts to increment the invalidated iterator, resulting in undefined behavior, and a crash.
The correct way to do this, actually, would be:
i=erase(i);
rather than using post-increment.
I'll avoid repeating what others have stated and instead suggest a more elegant solution via the erase-remove idiom:
myList.erase(std::remove_if(myList.begin(), myList.end(), [&name](auto& el) {
return el.first == name;
}), myList.end());
The reason is that the iterator breaks when you remove an item, but in the first case you are returning back so the error never occurs in the next iteration. In any way it's not a matter of your struct.
So I'm pretty new to C++, so I wanted to loop through a multidimensional vector that I have, but I'm getting errors such as
stl_algo.h
error: no match for 'operator==' (operand types are std::vector<std::basic_string<char> >' and 'const std::basic_string<char>'
There is lots of errors, here is the code:
mClass.h
std::vector<std::vector<std::string> > aData;
mClass.cpp
bool mClass::checkVector(std::string k)
{
if (std::find(mClass::aData.begin(), mClass::aData.end(), k) != mClass::aData.end())
{
return true;
}
return false;
}
mClass::aData.begin() and mClass.aData.end() return iterators over vectors, not iterators over strings. There is no operator == to compare a vector and a string. Hence the error.
You'll need to iterate through the vectors. Assuming you have C++11 support:
bool mClass::checkVector(std::string const& k)
{
for (auto const& vec : aData) { // Each `vec` is a std::vector<std::string>
for (auto const& str : vec) { // Each `str` is a std::string
// compare string represented by it2
if (str == k) {
return true;
}
}
}
return false;
}
The (pre-c++11) solution is to iterate through the vectors first:
Note the typedef for convenience.
typedef std::vector<std::vector<std::string> > vvec;
bool mClass::checkVector(std::string k)
{
for(vvec::const_iterator it=mClass::aData.begin(), end=mClass::aData.end();it!=end;++it){
const std::vector<std::string>& inner=*it;
if (std::find(inner.begin(), inner.end(), k) != inner.end())
{
return true;
}
}
return false;
}
Because you have a multidimensional array you can't use an iterator to go through every value so the best way to do it is to make a loop first
for(std::vector<std::vector<std::string> >::iterator it = Class::aData.begin(); it!=mClass::aData.end(); ++it)
if (std::find(it->begin(), it->end(), k) != it->end())
{
return true;
}
return false;
You can use std::find only on the lowest level of iterator, not on an iterator on a full vector.
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.