I have the following code in a function.
events is a global variable:
map<string, map<string, deque<Event> > > events;
this is my function:
void remove_events(vector<Event> c_events)
{
vector<Event>::iterator it1;
deque<Event>::iterator it2;
for(it1 = c_events.begin(); it1 != c_events.end(); ++it1)
{
it2 = events[it1->device][it1->dev_id].begin();
while(it2 != events[it1->device][it1->dev_id].end())
{
if(*it1 == *it2)
{
it2 = events[it1->device][it1->dev_id].erase(it2);
}
else ++it2;
}
}
}
c_events contains the objects I want to remove from events. However, the objects still remain in events after I tried removing them.
Let me know if you need me to post more code.
Related
I tried to follow the erase example for sample iterator over list but I can't make it work.
Here is the code so far:
for (list<list<string>>::iterator itr = listOfList.begin(), ; itr != listeOfListe.end(); itr++){
if (condition) {
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); it6++)
{
itr.erase(*it6);
}
}
}
I get the following error:
class "std::_List_iterator<std::__cxx11::list<std::string, std::allocator<std::string>>>" has no member "erase"
Which indicates that it considers that itr is a list of list of string, why isn't itr simply a list of string since it iterates over listOfList?
You are trying to call erase() on an iterator itself, not on the list that the iterator refers to. That is why you are getting the compiler error. You need to dereference the iterator via operator* or operator-> to access the list to call erase() on.
Also, your inner loop is not accounting for the fact that list::erase() invalidates the specified iterator, so your use of it6++ is undefined behavior after calling erase(it6). You need to use the iterator that erase() returns in order to continue the loop correctly, eg: it6 = erase(it6).
Try something more like this instead:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
list<string>::iterator it6 = itr->begin();
while (it6 != itr->end()) {
it6 = itr->erase(it6);
}
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
auto it6 = listOfStrings.begin();
while (it6 != listOfStrings.end()) {
it6 = listOfStrings.erase(it6);
}
}
}
Which simplifies to this:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
itr->clear();
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
listOfStrings.clear();
}
}
Alternatively:
// pre C++11
if (condition) {
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
itr->clear();
}
}
// C++11 and later
if (condition) {
for (auto &listOfStrings : listOfList){
listOfStrings.clear();
}
}
I don't think that is what you are looking for, though. Your title says you want to "remove a string from a list", so you are probably looking for something more like this instead:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
itr->remove(string); // removes all matching strings
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
listOfStrings.remove(string); // removes all matching strings
}
}
Alternatively:
// pre C++11
if (condition) {
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
itr->remove(string); // removes all matching strings
}
}
// C++11 or later
if (condition) {
for (auto &listOfStrings : listOfList){
listOfStrings.remove(string); // removes all matching strings
}
}
Or, maybe you are looking for something more like this instead?
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
list<string>::iterator it6 = itr->begin();
while (it6 != itr->end()) {
if (condition(*it6)) {
it6 = itr->erase(it6);
} else {
++it6;
}
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
auto it6 = listOfStrings.begin();
while (it6 != itr->end()) {
if (condition(*it6)) {
it6 = listOfStrings.erase(it6);
} else {
++it6;
}
}
}
Which, in that latter case, simplifies to this in C++20:
for (auto &listOfStrings : listOfList){
std::erase_if(listOfStrings, [](string &s){ return condition(s); });
}
Please note that behavior of this program is undefined.
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); it6++) {
itr->erase(it6); // !!
}
Code marked with // invalidates iterator it6. Incrementing it in the loop is now illegal, since only valid list iterators can be incremented.
To correct the snippet, use following:
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); ) {
it6 = itr->erase(it6);
}
In this example, we use the fact that std::list::erase returns the iterator to the next element after the one erased.
However, this is only for illustration, assuming you would want to learn how to work with iterators. Better code would simply be
itr->clear();
I'm trying to remove elements that have the same key and value in a multimap. This is my code for now. After deleting the element, I get exception.
multimap<string, CStudent> m_StudentMap;
void removeDuplicates() {
for (auto it1 = m_StudentMap.begin(); it1 != --m_StudentMap.end(); it1++) {
for (auto it2 = next(it1, 1); it2 != m_StudentMap.end(); it2++) {
if (it1->first == it2->first) {
if (it1->second == it2->second) {
m_StudentMap.erase(it2);
}
}
}
}
}
You were nearly right, but the trick with erasing elements in maps while iterating is to capture the new iterator returned by erase. I've also generalised the function so it can be used on an argument rather than being limited to m_StudentMap, and stopped the inner loop as soon as the keys diverge.
template <typename K, typename V>
void removeDuplicates(std::multimap<K, V>& mmap)
{
if (mmap.size() < 2) return;
for (auto it = mmap.begin(); it != prev(mmap.end()); ++it)
for (auto it2 = next(it); it2 != mmap.end() && it2->first == it->first; )
if (it->second == it2->second)
it2 = mmap.erase(it2);
else
++it2;
}
You can see it run / fork it etc. here.
I'm currently trying to delete 2 elements from a vector if some condition is met. I can successfully remove a single element without the "vector iterator not dereferencable" error occuring, I know the problem is been caused by removing two elements at once which messes up with the Iterators but am unsure as to the correct way of removing more than one element at once.
vector<SomeObj*> objs;
vector<SomeObj*>::iterator it = objs.begin();
while (it != objs.end())
{
vector<SomeObj*>::iterator it2 = objs.begin();
bool deleted = 0;
while (it2 != objs.end())
{
if ((*it)->somecondition(**it2))
{
delete *it2;
*it2 = NULL;
it = objs.erase(it2);
delete *it;
*it = NULL;
it = objs.erase(it); //Will error here due to invalidating the iterator
deleted = 1;
break;
}
++it2;
}
if (!deleted)
++it;
}
The problem is that the first call to erase() might very well invalidate the other iterator. See this post for a quick summary of what gets invalidated when in various containers. I'd say the simplest solution is to first traverse the container and mark the entries to be erased but do not erase them, and then in a second scan just erase everything that was marked. For performance reasons in this second scan you should either use std::remove_if or use reverse iterator.
Working with nested iterators is tricky if you are mutating the container.
I've put together some sample code that does what you are wanting. What I'm doing is delaying the removal by setting the elements to be removed to nullptr and then removing those as we encounter them in the loops.
#include <iostream>
#include <vector>
class Example
{
public:
Example(int size) : size(size) {}
bool somecondition(const Example& other) const
{
return size == other.size;
}
int size;
};
int main()
{
std::vector<Example*> vec;
vec.push_back(new Example(1));
vec.push_back(new Example(2));
vec.push_back(new Example(3));
vec.push_back(new Example(2));
for (auto it1 = vec.begin(); it1 != vec.end();)
{
if (!*it1)
{
it1 = vec.erase(it1);
continue;
}
for (auto it2 = vec.begin(); it2 != vec.end(); ++it2)
{
if (!*it2)
{
vec.erase(it2);
// we need to start the outer loop again since we've invalidated its iterator
it1 = vec.begin();
break;
}
if (it1 != it2 && (*it1)->somecondition(**it2))
{
delete *it1;
*it1 = nullptr;
delete *it2;
*it2 = nullptr;
break;
}
}
++it1;
}
for (auto example : vec)
{
std::cout << example->size << std::endl;
}
return 0;
}
Check if map in C++ contains all the keys from another map answers my question but I'm not sure how we iterate through two maps at the same time.
I know how to iterate through one as shown:
typedef std::map<QString, PropertyData> TagData;
TagData original = readFileToMap("FoxHud.bak");
for (TagData::const_iterator tagIterator = original.begin(); tagIterator != original.end(); tagIterator++) {
}
Try this way:
// As std::map keys are sorted, we can do:
typedef std::map<string, int> TagData;
TagData map1;
TagData map2;
...
TagData::const_iterator map1It = map1.begin();
TagData::const_iterator map2It = map2.begin();
bool ok = true;
std::size_t cnt = 0;
while (map2It != map2.end() && map1It != map1.end()) {
if (map1It->first != map2It->first) {
map1It++;
} else {
map2It++;
cnt++;
}
}
if (cnt != map2.size()) ok = false;
cout << "OK = " << ok << endl;
This should work with maps that are not the same size, as well.
If you want to iterate the 2 maps simultaneously, you can do this:
if (map1.size() != map2.size())
; // problem
else
{
for (map<X,Y>::const_iterator it1 = map1.begin(),
it2 = map2.begin();
it1 != map1.end() && it2 != map2.end();
++it1 , ++it2)
{
// ...
}
}
Now if you want to iterate through the 2 maps at different "speeds", then a while loop to condition the increments of it1 and it2 independently would then be more appropriate. See Golgauth's answer for an example.
My STL is a bit rusty, so forgive me for asking a possibly trivial question. Consider the following piece of code:
map<int,int> m;
...
for (auto itr = m.begin(); itr != m.end(); ++itr) {
if (itr->second == 0) {
m.erase(itr);
}
}
The question is: Is it safe to erase elements while looping over the map?
Yes, but not the way you do it. You're invalidating itr when you erase, then incrementing the invalid iterator.
auto itr = m.begin();
while (itr != m.end()) {
if (itr->first == 0) {
m.erase(itr++);
} else {
++itr;
}
}
I think that you shouldn't use removed iterator at all - in case of lists this causes serious problems, shouldn't be different for maps.
EDIT by Matthieu M: this code is well-formed in C++0x and allowed as an extension by MSVC.
map<int,int> m;
...
auto itr = m.begin();
while (itr != m.end())
{
if (itr->second == 0) {
itr = m.erase(itr);
}
else
{
itr++;
}
}
For the example given, It would actually be easier to use the erase overload that takes a key as an argument. This function erases all elements in the map with the given key (for a map, this is always either zero or one element)
map<int,int> m;
// ...
m.erase(0); // erase all elements with key equivalent to 0