How to Remove from a Map While Iterating It

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



Leave a reply



Submit