Remove entries from a map while iterating it in C++
This post will discuss how to remove entries from a map while iterating it in C++.
The idea is to iterate the map using iterators and call the unordered_map::erase function on the iterators that match the predicate. Since calling the erase() function invalidates the iterator, we can use the return value of erase() to set the iterator to the next element in the sequence.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <iostream> #include <unordered_map> #include <unordered_set> #include <string> template<typename K, typename V> void remove_keys(std::unordered_map<K, V> &m, const std::unordered_set<K> &keys) { auto it = m.cbegin(); while (it != m.cend()) { if (keys.find(it->first) != keys.cend()) { // supported in C++11 it = m.erase(it); } else { ++it; } } } int main() { std::unordered_map<std::string, std::string> u_map = { {"C", "C99"}, {"C++", "C++17"}, {"Java", "Java SE 9"}, {"PHP", "PHP 7.0"} }; std::unordered_set<std::string> keys = { "Java", "PHP" }; remove_keys(u_map, keys); for (const auto &entry: u_map) { std::cout << "{" << entry.first << ", " << entry.second << "}" << std::endl; } return 0; } |
Output:
{C++, C++17}
{C, C99}
The above approach won’t work before C++11 as the erase() function doesn’t return anything in C++98/03. The workaround is to post-increment the iterator while passing to the erase() function. This increments the iterator before it is invalidated by the erase() function and can be used in the next iteration of the loop.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
template<typename K, typename V> void remove_keys(std::unordered_map<K, V> &m, const std::unordered_set<K> &keys) { auto it = m.cbegin(); while (it != m.cend()) { if (keys.find(it->first) != keys.cend()) { // before C++11 m.erase(it++); } else { ++it; } } } |
Another feasible solution that works exactly like the previous approach is to make an explicit copy of the iterator increment it and call the erase() function on the copy.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
template<typename K, typename V> void remove_keys(std::unordered_map<K, V> &m, const std::unordered_set<K> &keys) { auto it = m.cbegin(); while (it != m.cend()) { auto curr = it++; if (keys.find(curr->first) != keys.cend()) { m.erase(curr); } } } |
We can also maintain a ‘to-be-removed-list’ of iterators to entries that satisfy the predicate. Then we loop through that list and call set::erase on each iterator.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void remove_keys(std::unordered_map<std::string, std::string> &m, const std::unordered_set<std::string> &keys) { std::list<std::unordered_map<std::string, std::string>::const_iterator> itrs; for (auto it = m.cbegin(); it != m.cend(); it++) { if (keys.find(it->first) != keys.cend()) { itrs.push_back(it); } } for (auto it: itrs) { m.erase(it); } } |
That’s all about removing entries from a map while iterating it in C++.
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)