Erase elements from a Set while Iterating in C++ & Generic erase_if()

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, 

 

Advertisements

 

Do you want to Learn Modern C++ from best?

We have curated a list of Best C++ Courses, that will teach you the cutting edge Modern C++ from the absolute beginning to advanced level. It will also introduce to you the word of Smart Pointers, Move semantics, Rvalue, Lambda function, auto, Variadic template, range based for loops, Multi-threading and many other latest features of C++ i.e. from C++11 to C++20.

Check Detailed Reviews of Best Modern C++ Courses

Remember, C++ requires a lot of patience, persistence, and practice. So, start learning today.

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top