In this article we will discuss how to erase elements from a set while iterating over it.

Erasing elements from std::set while Iterating

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,

Creating generic erase_if for std::set

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,