How to remove from a map while iterating it?
The standard associative-container erase idiom:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
Note that we really want an ordinary for
loop here, since we are modifying the container itself. The range-based loop should be strictly reserved for situations where we only care about the elements. The syntax for the RBFL makes this clear by not even exposing the container inside the loop body.
Edit. Pre-C++11, you could not erase const-iterators. There you would have to say:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Erasing an element from a container is not at odds with constness of the element. By analogy, it has always been perfectly legitimate to delete p
where p
is a pointer-to-constant. Constness does not constrain lifetime; const values in C++ can still stop existing.
iterating over and removing from a map
As of Java 8 you could do this as follows:
map.entrySet().removeIf(e -> <boolean expression>);
Oracle Docs: entrySet()
The set is backed by the map, so changes to the map are reflected in the set, and vice-versa
How to remove a key from HashMap while iterating over it?
Try:
Iterator<Map.Entry<String,String>> iter = testMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String,String> entry = iter.next();
if("Sample".equalsIgnoreCase(entry.getValue())){
iter.remove();
}
}
With Java 1.8 and onwards you can do the above in just one line:
testMap.entrySet().removeIf(entry -> "Sample".equalsIgnoreCase(entry.getValue()));
ES6: Is it dangerous to delete elements from Set/Map during Set/Map iteration?
Yes, you can simplify to that, it's totally safe.
- Sets and Maps are always iterated in insertion order
- Deleting an item does not affect the position of any iterator - you can visualise the shape of the collection not being changed, just being emptied.
- So: elements that are deleted and have not yet been iterated won't be iterated
- Elements that have already been iterated and are deleted (like in your case) won't affect anything but other iterations/lookups.
- Elements that are added (and are not already part of the collection) during the iteration will always be iterated
From that last point follows that the only dangerous thing to do would be something like
const s = new Set([1]);
for (let x of s) {
s.delete(x);
s.add(1);
}
but not because of undefined behaviour or memory accumulation, but because of the infinite loop.
Delete elements from Hashmap while iterating over it
To remove a element whilst in the middle of iterating, use Iterator.remove()
.
Remove key/value from map while iterating
This should be a bit more efficient than Tim's answer (because you only need to iterate over the map once). Unfortunately, it is also pretty verbose
def map = [2:1, 3:4]
def iterator = map.entrySet().iterator()
while (iterator.hasNext()) {
if (iterator.next().value - 1 <= 0) {
iterator.remove()
}
}
// test that it worked
assert map == [3:4]
Erase other elements of std::map while iterating
You need to read the iterator invalidation rules:
std::map::erase
References and iterators to the erased elements are invalidated. Other
references and iterators are not affected.
So as long as you don't use the iterator where you erased, you are good.
What happens if you call erase() on a map element while iterating from begin to end?
C++11
This has been fixed in C++11 (or erase has been improved/made consistent across all container types).
The erase method now returns the next iterator.
auto pm_it = port_map.begin();
while(pm_it != port_map.end())
{
if (pm_it->second == delete_this_id)
{
pm_it = port_map.erase(pm_it);
}
else
{
++pm_it;
}
}
C++03
Erasing elements in a map does not invalidate any iterators.
(apart from iterators on the element that was deleted)
Actually inserting or deleting does not invalidate any of the iterators:
Also see this answer:
Mark Ransom Technique
But you do need to update your code:
In your code you increment pm_it after calling erase. At this point it is too late and is already invalidated.
map<string, SerialdMsg::SerialFunction_t>::iterator pm_it = port_map.begin();
while(pm_it != port_map.end())
{
if (pm_it->second == delete_this_id)
{
port_map.erase(pm_it++); // Use iterator.
// Note the post increment.
// Increments the iterator but returns the
// original value for use by erase
}
else
{
++pm_it; // Can use pre-increment in this case
// To make sure you have the efficient version
}
}
Related Topics
Difference Between _Tmain() and Main() in C++
C++ Templates That Accept Only Certain Types
Does the Size of an Int Depend on the Compiler And/Or Processor
What Does the Question Mark Character ('') Mean in C++
Is There a Difference Between Foo(Void) and Foo() in C++ or C
Why Isn't Vector≪Bool≫ a Stl Container
Can Someone Explain This Template Code That Gives Me the Size of an Array
What Is Iaca and How to Use It
Can a C++ Class Include Itself as an Member
Is (4 ≫ Y ≫ 1) a Valid Statement in C++? How to Evaluate It If So
Developing C Wrapper API For Object-Oriented C++ Code
C++ Static Initialization Order
Why Is the Type of the Main Function in C and C++ Left to the User to Define