Increment an iterator standard map - c++

ALL,
std::map<int, std::string> addressee;
std::map<int, std::string>::iterator it1, it2;
for( it1 = addressee.begin(); it1 != addressee().end(); it1++ )
{
bool found = false;
for( it2 = it1 + 1; it2 != addressee.end() && !found; it2++ )
{
if( it1->second == it1->second )
{
printf( "Multiple occurences of addressees found" );
found = true;
}
}
}
gcc spits out an error: no match for operator+.
This code is a simplified version of what I'm trying to do right now. I guess I can use std::advance(), but it seems it just going to be a waste of the function call.
Is there a better fix for that?

std::map does not have random access iterators, only bidirectional iterators, so there's no + n operation. Instead, use std::next:
#include <iterator>
#include <map>
// ...
for (auto it1 = addressee.begin(), e = addressee.end(); it1 != e; ++it1)
{
for (auto it2 = std::next(it1); it2 != e; ++it2)
{
if (it1->second == it2->second)
{
// ...
break;
}
}
}
In fact, you should always use std::next, since it knows which iterator category its argument has and what the most efficient way to compute the next iterator is. That way, you don't have to care about the specific container you happen to be using.

#Kerrek has already pointed out how to handle the problem you're having at the syntactic level.
I'm going to consider the problem at a more algorithmic level--what you're really trying to accomplish overall, rather than just looking at how to repair that particular line of the code.
Unless the collection involved is dependably tiny so the efficiency of this operation doesn't matter at all, I'd make a copy of the mapped values from the collection, then use sort and unique on it to see if there are any duplicates:
std::vector<std::string> temp;
std::transform(addressee.begin(), addressee.end(),
std::back_inserter(temp),
[](std::pair<int, std::string> const &in) { return in.second; });
std::sort(temp.begin(), temp.end());
if (std::unique(temp.begin(), temp.end()) != temp.end()) {
std::cout << "Multiple occurrences of addressees found";
found = true;
}
This reduces the complexity from O(N2) to O(N log N), which will typically be quite substantial if the collection is large at all.

Related

Converting const auto & to iterator

A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.

Vector Iterator erase two elements on condition

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

Efficient way to delete a value from an unordered_map, when given just the value C++

I have an abstract class called Object and I am using std::unordered_map<int, Object*> objects to contain these Objects within a class called DataSet. Each object has an id associated with it.
Normally, when deleting an object from my unordered_map, I can just do iterator = find(id) and then call erase on that iterator.
This is easy and efficient. The problem is, I have to implement a method to delete an entry/pair by value, rather then by the key (which was my id). This gives me the following prototype:
int DataSet::DeleteObject(Object* object)
What is the most efficient way of accomplishing this though? I'm thinking I can do something like this:
if(object){
for(auto kv : objects) {
if(kv.second == object) {
objects.erase(kv);
}
}
return 1;
}
But it seems very inefficient. So what would be the most efficient way to accomplish this?
Don't perform the lookup twice; erase via iterator:
for (auto it = m.begin(); it != m.end(); )
{
if (it->second == needle) { m.erase(it++); }
else { ++it; }
}
This deletes all occurrences of needle. If you want to erase at most the first occurrence, a simpler loop will do:
for (auto it = m.begin(); it != m.end(); ++it)
{
if (it->second == needle) { m.erase(it); break; }
}
If you want to erase exactly one element, you need to add a check that you found any needles. This can be achieved with find_if, which may also be used as a variation of the previous algorithm:
auto it = std::find_if(m.begin(), m.end(),
[&needle](const auto & p) { return p.second == needle; });
if (it != m.end()) { m.erase(it); }
else { /* no such element! */ }

Erasing the last element of a vector by looping through it

I want to loop through a vector and erase certain elements that correspond to a certain criteria, for example:
vector<int> myvector;
vector<int>::iterator it;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
for(it = myvector.begin(); it != myvector.end(); ++it){
if((*it) == 4){
it = myvector.erase(it);
}
}
Now this works fine unless the criterion erases the last item like in the code above. How do you avoid this behaviour ?
Thanks.
EDIT------------------------------------
Now the reason I was looping through it was that there are actually 4 vectors I need to delete the element from (but the criterion is only on one vector):
In this case, is this how to go ?
vector<int> myvector;
vector<int> myvector2;
vector<int> myvector3;
vector<int> myvector4;
vector<int>::iterator it;
vector<int>::iterator it2;
vector<int>::iterator it3;
vector<int>::iterator it4;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
(assume myvector2/3/4 have values inside them)
it2 = myvector2.begin()
it3 = myvector3.begin()
it4 = myvector4.begin()
for(it = myvector.begin(); it != myvector.end();){
if((*it) == 4){
it = myvector.erase(it);
it2 = myvector2.erase(it2);
it3 = myvector3.erase(it3);
it4 = myvector4.erase(it4);
}
else{
++it;
++it2;
++it3;
++it4;
}
}
Is there a modification to the erase/remove idiom valid in this case ?
The usual is the remove/erase idiom, which would look something like this:
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
Edit: Rereading your question, you mention "certain criteria". If the criteria aren't necessarily just removing a single value, you can use std::remove_if instead of std::remove, and specify your criteria in a functor.
Edit2: for the version dealing with four vectors, the usual method is to create a struct holding the four related values, and delete entire structs:
struct x4 {
int a, b, c, d;
// define equality based on the key field:
bool operator==(x4 const &other) { return a == other.a; }
x4(int a_, int b_=0, int c_=0, ind d_=0) : a(a_), b(b_), c(c_), d(d_) {}
};
std::vector<x4> myvector;
myvector.erase(std::remove(myvector.begin(), myvector.end(), x4(4));
Again, if your criteria are more complex than you can easily express in a comparison operator, you can use std::remove_if instead of std::remove. This is also useful if/when you might need to apply different criteria at different times.
If you really need to keep your data in parallel vectors (e.g., you're feeding the data to something external that requires separate, contiguous arrays), then using a loop is probably as good as the alternatives.
Don't do this with a for loop, there's already a well-debugged algorithm for you.
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
I think you should write the loop as :
for(it = myvector.begin(); it != myvector.end(); )
{
if((*it) == 4)
it = myvector.erase(it);
else
++it; //increment here!
}
Because in your code, if you find 4, you update it in the if block itself, but after that you again increment/update it in the for also which is wrong. That is why I moved it to else block that ensures that it gets incremented if you don't find 4 (or whatever value you're searching).
Also remember that erase returns iterator pointing to the new location of the element that followed the last element erased by the function call.
erase is generally used with remove (Also have a look at erase-remove idiom) as shown below
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
for(it = myvector.begin(); it < myvector.end(); ++it){
if((*it) == 4){
it = myvector.erase(it);
}
}
This will make sure your loop will break if the it >= myvector.end().

Removing elements from a C++ map through a for-loop

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