In this article we will discuss how to erase elements from a set while iterating over it.
std::set provides a member function erase() that accepts an iterator as an argument and deletes it from the set i.e.
iterator erase (const_iterator position);
But it makes the passed iterator invalid. From c++11 onward it returns an iterator that points to the element next to last deleted element. We can use this for further iteration.
Let’s see an example,
Suppose we have a set of strings i.e.
// Set of Strings std::set<std::string> setOfStrs = {"Hi", "Hello", "is", "the", "at", "Hi", "is", "from", "that"};
Now lets remove all string entries from string whose length is greater than 3 i.e.
// Create an iterator pointing to start of set std::set<std::string>::iterator it = setOfStrs.begin(); // Iterate over the set till end while(it != setOfStrs.end()) { if(it->size() > 3) { // Remove string from set if length is greater than 3. it = setOfStrs.erase(it); } else it++; }
Complete example is as follows,
#include <iostream> #include <set> #include <iterator> #include <string> #include <vector> int main() { // Set of Strings std::set<std::string> setOfStrs = {"Hi", "Hello", "is", "the", "at", "Hi", "is", "from", "that"}; // Printing Set on console std::copy (setOfStrs.begin(), setOfStrs.end(), std::ostream_iterator<std::string>(std::cout, ", ")); std::cout<<std::endl; // Create an iterator pointing to start of set std::set<std::string>::iterator it = setOfStrs.begin(); // Iterate over the set till end while(it != setOfStrs.end()) { if(it->size() > 3) { // Remove string from set if length is greater than 3. it = setOfStrs.erase(it); } else it++; } // Printing Set on console std::copy (setOfStrs.begin(), setOfStrs.end(), std::ostream_iterator<std::string>(std::cout, ", ")); std::cout<<std::endl; return 0; }
Output:
Hello, Hi, at, from, is, that, the, Hi, at, is, the,
Let’s create a generic function that can be used with set, vector or list to remove elements while iterating based on some condition i.e.
/* * Creating a generic function that will erase elements from container * while iterating if any given condition matches. * * It accepts following arguments * * 1.) Container i.e. set, list or vector * 2.) Iterator pointing to start of range * 3.) Iterator pointing to end of range * 4.) Callback to check if it needs to delete the current element * * It Iterates over the range and check if element needs to be deleted using passed checker callback. * If Yes then it deletes the element */ template <typename S, typename T> void erase_if(S & container, T first, T last, std::function<bool (T)> checker) { while(first != last) { if(checker(first)) { first = container.erase(first); } else first++; } }
Now let’s use the above generic function to erase all string from set whose size is greater than 2 i.e.
// Set Of String std::set<std::string> setOfStrs = {"Hi", "Hello", "is", "the", "at", "Hi", "is", "from", "that"};
typedef std::set<std::string>::iterator SetIter ; // Callback to check if size of string is greater than 2. std::function<bool (SetIter)> lambda = [](SetIter it) -> bool { return it->size() > 2; };
// Remove all strings from set whose length is greater than 3. erase_if<>(setOfStrs, setOfStrs.begin(), setOfStrs.end(), lambda);
Complete example is as follows,
#include <iostream> #include <set> #include <iterator> #include <string> #include <functional> #include <vector> /* * Creating a generic function that will erase elements from container * while iterating if any given condition matches. * * It accepts following arguments * * 1.) Container i.e. set, list or vector * 2.) Iterator pointing to start of range * 3.) Iterator pointing to end of range * 4.) Callback to check if it needs to delete the current element * * It Iterates over the range and check if element needs to be deleted using passed checker callback. * If Yes then it deletes the element */ template <typename S, typename T> void erase_if(S & container, T first, T last, std::function<bool (T)> checker) { while(first != last) { if(checker(first)) { first = container.erase(first); } else first++; } } int main() { typedef std::set<std::string>::iterator SetIter ; // Set Of String std::set<std::string> setOfStrs = {"Hi", "Hello", "is", "the", "at", "Hi", "is", "from", "that"}; // Printing Set Contents std::copy(setOfStrs.begin(), setOfStrs.end(), std::ostream_iterator<std::string>(std::cout, ", ")); std::cout<<std::endl; // Callback to check if size of string is greater than 2. std::function<bool (SetIter)> lambda = [](SetIter it) -> bool { return it->size() > 2; }; // Remove all strings from set whose length is greater than 3. erase_if<>(setOfStrs, setOfStrs.begin(), setOfStrs.end(), lambda); // Printing Set Contents std::copy(setOfStrs.begin(), setOfStrs.end(), std::ostream_iterator<std::string>(std::cout, ", ")); std::cout<<std::endl; return 0; }
Output:
Hello, Hi, at, from, is, that, the, Hi, at, is,