C++ vector - find ("no match for 'operator =='") - c++

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.

Related

How to delete an object from a map which contains a vector as value in C++

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;
}
}

Differences in erasing an entity from a list in C++

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.

How to sort a copy of a C++ vector depending on position of elements in the original vector?

I do have this map in my class :
map<string,vector<pair<string,int>>> assessedTest__name_result;
And I have copied the vector to another, temporary one, which I would like to sort by the first value in the pair. In case there are two elements with the same first value in the pair I would like to determine the position by their position in the original vector. But I if I want to look at the original vector the static vector sorting function won't allow me to do that. Could you please tell me how to do it ?
Thanks
The code :
//test is the key in the map shown above
vector<pair<int,int>> tmpVec = assessedTest__name_result.at(test);
sort (tmpVec.begin(),tmpVec.end(),vecIdSort);
...
bool myClass::vecNameSort(const pair<string,int>& firstElem, const pair<string,int>& secondElem, const string& test){
if (firstElem.first < secondElem.first)
return true;
if (firstElem.first > secondElem.first)
return false;
return AssSort(firstElem.first, secondElem.first,test); //in case the elements are the same
}
bool myClass::AssSort (const int firstId, const int secId,const string& test){
bool foundFirst,foundSecond;
vector<pair<string,int>>::iterator first,second;
for (auto it = assessedTest__name_result.at(test).begin();it != assessedTest__name_result.at(test).end(); it++){
if ((*it).first == firstId){
first = it;
foundFirst = true;
}
if ((*it).first == secId){
second = it;
foundSecond = true;
}
if (foundFirst && foundSecond) break;
}
return first < second;
}
std::stable_sort is the answer
std::stable_sort(tmpVec.begin(),tmpVec.end())
If you have your own comparison function vecIdSort do this
std::stable_sort (tmpVec.begin(),tmpVec.end(),vecIdSort);
I used a lambda function and it worked.

Compare multimap with String

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

vector erase expects 2 arguments error

I am using Code::Blocks and here is my code below in c++.
void removeEvenLength(vector<string> &vec)
{
for (vector<string>::reverse_iterator i = vec.rbegin(); i != vec.rend(); ++i)
{
string word = *i;
if (word.length() % 2 == 0)
vec.erase(i);
}
}
This function takes a vector of strings and removes the word that is even letters long. I get an error that says erase needs 2 arguments but according to the c++ documentation, one should be okay. Do you know what is wrong?
If you absolutely must use erase() with a reverse iterator, here's how:
void removeEvenLength(vector<string> &vec)
{
for (vector<string>::reverse_iterator i = vec.rbegin(); i != vec.rend();)
{
string word = *i;
if (word.length() % 2 == 0) {
vector<string>::iterator newi = vec.erase(i.base() - 1);
i = vector<string>::reverse_iterator(newi);
} else {
++i;
}
}
}
But that's not the best way to remove all even-length strings in a vector, for a number of reasons. The way to do that is as follows (it's called the "erase-remove" idiom):
struct IsEven {
bool operator()(const string &s) const {
return (s.size() % 2) == 0;
}
};
vec.erase(
std::remove_if(vec.begin(), vec.end(), IsEven()),
vec.end()
);
Or in C++11 you can use a lambda instead of a separate functor:
vec.erase(
std::remove_if(
vec.begin(), vec.end(),
[](const string &s) { return (s.size() % 2) == 0; }
),
vec.end()
);
If you see e.g. this reference you will see that erase takes an iterator or a const_iterator, not a reverse_iterator. So you can't use it when iterating backwards.
Also, as suggested by chris in a comment, you might want to use std::remove_if instead. The linked reference contains an example how to use it.