Iterate over container in either direction AND erase elements - c++

I have two std::maps:
std::map<int,int> map1;
std::map<int,int> map2;
I need to iterate over one backwards and the other forwards (because that is the pattern of data access). Whilst iterating I would like to be able to erase elements continue iterating.
I would like to use the same method.
I have seen examples using templates showing how to iterate bidirectionally, but doesn't demonstrate erasing elements (and this is important because erase() only works with forward iterators):
Iterating over a container bidirectionally
and I have seen reverse_iterator examples which erase, but they aren't bidirectional:
How to call erase with a reverse iterator using a for loop
but I would like to iterate bidirectionally and erase?

You can write a function for a std::map, std::set or std::list:
template<typename Cont, typename Pred>
void bidir_remove_if( Cont &c, Pred p, bool forward )
{
auto b = c.begin();
auto e = c.end();
while( b != e ) {
auto it = forward ? b++ : --e;
if( p(*it) ) {
auto end = b == e;
( forward ? it : e ) = c.erase( it );
if( end ) break;
}
}
}
live example
note - you cannot use this function for std::vector due to invalidation of iterators (and you should not as you better use erase-remove idiom for it).

Related

Implementing stable_partition for forward_list

I want to implement something similar to std::stable_partition but for forward_list of c++11.
The stl version requires bidirectional iterators, however by utilizing container specific methods I believe I can get the same outcome effeciently.
Example declaration :
template <typename T, typename UnaryPredicate>
void stable_partition(std::forward_list<T>& list, UnaryPredicate p);
(while possible to add begin and end iterators, I omitted them for brevity. The same for returning the partition point )
I already worked out the algorithm to accomplish this on my own list type, but I have troubles implementing it in stl.
The key method appears to be splice_after. Other methods require memory allocations and copying elements.
Algorithm sketch :
create a new empty list. It will hold all elements p returns true on.
loop over the target list, add items to the true list in accordance to invoking p.
concat the true list to the beginning of the target list.
With proper coding this should be linear time (all operations inside the loop can be done in constant time) and without extra memory allocation or copying.
I am trying to implement the second step using splice_after, but I end up either concating the wrong element or invalidating my iterators.
The question:
What is the correct use of splice_after, so that I avoid
mixing iterators between lists and insert the correct elements?
First Attempt (how I hoped it works):
template <typename T, typename UnaryPredicate>
void stable_partition(std::forward_list<T>& list, UnaryPredicate p)
{
std::forward_list<T> positives;
auto positives_iter = positives.before_begin();
for (auto iter = list.begin(); iter != list.end(); ++iter)
{
if (p(*iter))
positives.splice_after(positives_iter, list, iter);
}
list.splice_after(list.before_begin(), positives);
}
Unfortunately this has at least one major flaw: splice_after inserts after iter, and the wrong element is inserted.
Also, when the element is moved to the other list, incrementing iter now traverses the wrong list.
Having to maintain the preceding iterators for std::forward_list::splice_after makes it a bit trickier, but still pretty short:
template<class T, class UnaryPredicate>
std::array<std::forward_list<T>, 2>
stable_partition(std::forward_list<T>& list, UnaryPredicate p) {
std::array<std::forward_list<T>, 2> r;
decltype(r[0].before_begin()) pos[2] = {r[0].before_begin(), r[1].before_begin()};
for(auto i = list.before_begin(), ni = i, e = list.end(); ++ni != e; ni = i) {
bool idx = p(*ni);
auto& p = pos[idx];
r[idx].splice_after(p, list, i);
++p;
}
return r;
}
Usage example:
template<class T>
void print(std::forward_list<T> const& list) {
for(auto const& e : list)
std::cout << e << ' ';
std::cout << '\n';
}
int main() {
std::forward_list<int> l{0,1,2,3,4,5,6};
print(l);
// Partition into even and odd elements.
auto p = stable_partition(l, [](auto e) { return e % 2; });
print(p[0]); // Even elements.
print(p[1]); // Odd elements.
}

Obtaining an iterator from the element passed to a lambda when using std::find_if

I am trying to simplify a recursive function that receives an iterator. Somewhere in the function it is necessary to search for an element matching a given condition in the range going from the iterator to the end of the vector. So, I thought I could use find_if as shown below:
typedef std::vector<Foo> FooVec;
FooVec v;
int f(FooVec::iterator it) {
/* ... */
auto it2 = std::find_if(it, end(v),
[](const Foo& foo) {
auto foo_it = /* obtain the corresponding iterator for foo. */
return f(foo_it) == 0;
});
/* ... */
}
But the lambda function receives an element, not an iterator to the current element, so I cannot easily call f again. I could search for foo in v in order to get the iterator, but that would be inefficient. Alternatively I could just use a regular for loop with the iterators. But I was wondering whether there is the possibility to use find_if in this situation.
Messy, but v.begin() + (&foo - &v.front()) is the iterator pointing to foo. Note that this only works because vector has contiguous storage: don't try it with a list or deque.
If I were you, I would just write the loop myself. (Yeah, I know, I usually say to use the algorithms, but this seems like a case where doing it yourself is easier).
Uncompiled code follows:
for ( auto iter = it; iter != end(v); ++iter )
{
// *iter is the value; iter is the iterator
// if you have to search to the end, you can use [iter, end(v))
}
Works with all containers: vector, list, deque, etc.

What could be reason it crashes when I use vector::erase?

I am trying to do some operation on vector. And calling erase on vector only at some case.
here is my code
while(myQueue.size() != 1)
{
vector<pair<int,int>>::iterator itr = myQueue.begin();
while(itr != myQueue.end())
{
if(itr->first%2 != 0)
myQueue.erase(itr);
else
{
itr->second = itr->second/2;
itr++;
}
}
}
I am getting crash in 2nd iteration.And I am getting this crash with message vector iterator incompatible .
What could be the reason of crash?
If erase() is called the iterator is invalidated and that iterator is then accessed on the next iteration of the loop. std::vector::erase() returns the next iterator after the erased iterator:
itr = myQueue.erase(itr);
Given an iterator range [b, e) where b is the beginning and e one past the end of the range for a vector an erase operation on an iterator i somewhere in the range will invalidate all iterators from i upto e. Which is why you need to be very careful when calling erase. The erase member does return a new iterator which you can you for subsequent operations and you ought to use it:
itr = myQueue.erase( itr );
Another way would be to swap the i element and the last element and then erase the last. This is more efficient since less number of moves of elements beyond i are necessary.
myQueue.swap( i, myQueue.back() );
myQueue.pop_back();
Also, from the looks of it, why are you using vector? If you need a queue you might as well use std::queue.
That is undefined behavior. In particular, once you erase an iterator, it becomes invalid and you can no longer use it for anything. The idiomatic way of unrolling the loop would be something like:
for ( auto it = v.begin(); it != v.end(); ) {
if ( it->first % 2 != 0 )
it = v.erase(it);
else {
it->second /= 2;
++it;
}
}
But then again, it will be more efficient and idiomatic not to roll your own loop and rather use the algorithms:
v.erase( std::remove_if( v.begin(),
v.end(),
[]( std::pair<int,int> const & p ) {
return p.first % 2 != 0;
}),
v.end() );
std::transform( v.begin(), v.end(), v.begin(),
[]( std::pair<int,int> const & p ) {
return std::make_pair(p.first, p.second/2);
} );
The advantage of this approach is that there is a lesser number of copies of the elements while erasing (each valid element left in the range will have been copied no more than once), and it is harder to get it wrong (i.e. misuse an invalidated iterator...) The disadvantage is that there is no remove_if_and_transform so this is a two pass algorithm, which might be less efficient if there is a large number of elements.
Iterating while modifying a loop is generally tricky.
Therefore, there is a specific C++ idiom usable with non-associative sequences: the erase-remove idiom.
It combines the use of the remove_if algorithm with the range overload of the erase method:
myQueue.erase(
std::remove_if(myQueue.begin(), myQueue.end(), /* predicate */),
myQueue.end());
where the predicate is expressed either as a typical functor object or using the new C++11 lambda syntax.
// Functor
struct OddKey {
bool operator()(std::pair<int, int> const& p) const {
return p.first % 2 != 0;
}
};
/* predicate */ = OddKey()
// Lambda
/* predicate */ = [](std::pair<int, int> const& p) { return p.first % 2 != 0; }
The lambda form is more concise but may less self-documenting (no name) and only available in C++11. Depending on your tastes and constraints, pick the one that suits you most.
It is possible to elevate your way of writing code: use Boost.Range.
typedef std::vector< std::pair<int, int> > PairVector;
void pass(PairVector& pv) {
auto const filter = [](std::pair<int, int> const& p) {
return p.first % 2 != 0;
};
auto const transformer = [](std::pair<int, int> const& p) {
return std::make_pair(p.first, p.second / 2);
};
pv.erase(
boost::transform(pv | boost::adaptors::filtered( filter ),
std::back_inserter(pv),
transformer),
pv.end()
);
}
You can find transform and the filtered adaptor in the documentation, along with many others.

remove_if equivalent for std::map

I was trying to erase a range of elements from map based on particular condition. How do I do it using STL algorithms?
Initially I thought of using remove_if but it is not possible as remove_if does not work for associative container.
Is there any "remove_if" equivalent algorithm which works for map ?
As a simple option, I thought of looping through the map and erase. But is looping through the map and erasing a safe option?(as iterators get invalid after erase)
I used following example:
bool predicate(const std::pair<int,std::string>& x)
{
return x.first > 2;
}
int main(void)
{
std::map<int, std::string> aMap;
aMap[2] = "two";
aMap[3] = "three";
aMap[4] = "four";
aMap[5] = "five";
aMap[6] = "six";
// does not work, an error
// std::remove_if(aMap.begin(), aMap.end(), predicate);
std::map<int, std::string>::iterator iter = aMap.begin();
std::map<int, std::string>::iterator endIter = aMap.end();
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
return 0;
}
Almost.
for(; iter != endIter; ) {
if (Some Condition) {
iter = aMap.erase(iter);
} else {
++iter;
}
}
What you had originally would increment the iterator twice if you did erase an element from it; you could potentially skip over elements that needed to be erased.
This is a common algorithm I've seen used and documented in many places.
[EDIT] You are correct that iterators are invalidated after an erase, but only iterators referencing the element that is erased, other iterators are still valid. Hence using iter++ in the erase() call.
erase_if for std::map (and other containers)
I use the following template for this very thing.
namespace stuff {
template< typename ContainerT, typename PredicateT >
void erase_if( ContainerT& items, const PredicateT& predicate ) {
for( auto it = items.begin(); it != items.end(); ) {
if( predicate(*it) ) it = items.erase(it);
else ++it;
}
}
}
This won't return anything, but it will remove the items from the std::map.
Usage example:
// 'container' could be a std::map
// 'item_type' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
return /* insert appropriate test */;
});
Second example (allows you to pass in a test value):
// 'test_value' is value that you might inject into your predicate.
// 'property' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4; // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
return item.property < test_value; // or whatever appropriate test
});
Now, std::experimental::erase_if is available in header <experimental/map>.
See: http://en.cppreference.com/w/cpp/experimental/map/erase_if
Here is some elegant solution.
for (auto it = map.begin(); it != map.end();)
{
(SomeCondition) ? map.erase(it++) : (++it);
}
For those on C++20 there are built-in std::erase_if functions for map and unordered_map:
std::unordered_map<int, char> data {{1, 'a'},{2, 'b'},{3, 'c'},{4, 'd'},
{5, 'e'},{4, 'f'},{5, 'g'},{5, 'g'}};
const auto count = std::erase_if(data, [](const auto& item) {
auto const& [key, value] = item;
return (key & 1) == 1;
});
I got this documentation from the excellent SGI STL reference:
Map has the important property that
inserting a new element into a map
does not invalidate iterators that
point to existing elements. Erasing an
element from a map also does not
invalidate any iterators, except, of
course, for iterators that actually
point to the element that is being
erased.
So, the iterator you have which is pointing at the element to be erased will of course be invalidated. Do something like this:
if (some condition)
{
iterator here=iter++;
aMap.erase(here)
}
The original code has only one issue:
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
Here the iter is incremented once in the for loop and another time in erase, which will probably end up in some infinite loop.
From the bottom notes of:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
a Pair Associative Container cannot provide mutable iterators (as defined in the Trivial Iterator requirements), because the value type of a mutable iterator must be Assignable, and pair is not Assignable. However, a Pair Associative Container can provide iterators that are not completely constant: iterators such that the expression (*i).second = d is valid.
First
Map has the important property that inserting a new element into a map does not invalidate iterators that point to existing elements. Erasing an element from a map also does not invalidate any iterators, except, of course, for iterators that actually point to the element that is being erased.
Second, the following code is good
for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}
When calling a function, the parameters are evaluated before the call to that function.
So when iter++ is evaluated before the call to erase, the ++ operator of the iterator will return the current item and will point to the next item after the call.
IMHO there is no remove_if() equivalent.
You can't reorder a map.
So remove_if() can not put your pairs of interest at the end on which you can call erase().
Based on Iron Savior's answer For those that would like to provide a range more along the lines of std functional taking iterators.
template< typename ContainerT, class FwdIt, class Pr >
void erase_if(ContainerT& items, FwdIt it, FwdIt Last, Pr Pred) {
for (; it != Last; ) {
if (Pred(*it)) it = items.erase(it);
else ++it;
}
}
Curious if there is some way to lose the ContainerT items and get that from the iterator.
Steve Folly's answer I feel the more efficient.
Here is another easy-but-less efficient solution:
The solution uses remove_copy_if to copy the values we want into a new container, then swaps the contents of the original container with those of the new one:
std::map<int, std::string> aMap;
...
//Temporary map to hold the unremoved elements
std::map<int, std::string> aTempMap;
//copy unremoved values from aMap to aTempMap
std::remove_copy_if(aMap.begin(), aMap.end(),
inserter(aTempMap, aTempMap.end()),
predicate);
//Swap the contents of aMap and aTempMap
aMap.swap(aTempMap);
If you want to erase all elements with key greater than 2, then the best way is
map.erase(map.upper_bound(2), map.end());
Works only for ranges though, not for any predicate.
I use like this
std::map<int, std::string> users;
for(auto it = users.begin(); it <= users.end()) {
if(<condition>){
it = users.erase(it);
} else {
++it;
}
}

Erasing items from an STL list

I want to make a function which moves items from one STL list to another if they match a certain condition.
This code is not the way to do it. The iterator will most likely be invalidated by the erase() function and cause a problem:
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); it++)
{
if(myCondition(*it))
{
myOtherList.push_back(*it);
myList.erase(it);
}
}
So can anyone suggest a better way to do this ?
Erase returns an iterator pointing to the element after the erased one:
std::list<MyClass>::iterator it = myList.begin();
while (it != myList.end())
{
if(myCondition(*it))
{
myOtherList.push_back(*it);
it = myList.erase(it);
}
else
{
++it;
}
}
STL lists have an interesting feature: the splice() method lets you destructively move elements from one list to another.
splice() operates in constant time, and doesn't copy the elements or perform any free store allocations/deallocations. Note that both lists must be of the same type, and they must be separate list instances (not two references to the same list).
Here's an example of how you could use splice():
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); ) {
if(myCondition(*it)) {
std::list<MyClass>::iterator oldIt = it++;
myOtherList.splice(myOtherList.end(), myList, oldIt);
} else {
++it;
}
}
Solution 1
template<typename Fwd, typename Out, typename Operation>
Fwd move_if(Fwd first, Fwd last, Out result, Operation op)
{
Fwd swap_pos = first;
for( ; first != last; ++first ) {
if( !op(*first) ) *swap_pos++ = *first;
else *result++ = *first;
}
return swap_pos;
}
The idea is simple. What you want to do is remove elements from one container and place them in another if a predicate is true. So take the code of the std::remove() algorithm, which already does the remove part, and adapt it to your extra needs. In the code above I added the else line to copy the element when the predicate is true.
Notice that because we use the std::remove() code, the algorithm doesn't actually shrink the input container. It does return the updated end iterator of the input container though, so you can just use that and disregard the extra elements. Use the erase-remove idiom if you really want to shrink the input container.
Solution 2
template<typename Bidi, typename Out, typename Operation>
Bidi move_if(Bidi first, Bidi last, Out result, Operation op)
{
Bidi new_end = partition(first, last, not1(op));
copy(new_end, last, result);
return new_end;
}
The second approach uses the STL to implement the algorithm. I personally find it more readable than the first solution, but it has two drawbacks: First, it requires the more-powerful bidirectional iterators for the input container, rather than the forward iterators we used in the first solution. Second, and this is may or may not be an issue for you, the containers are not guaranteed to have the same ordering as before the call to std::partition(). If you wish to maintain the ordering, replace that call with a call to std::stable_partition(). std::stable_partition() might be slightly slower, but it has the same runtime complexity as std::partition().
Either Way: Calling the Function
list<int>::iterator p = move_if(l1.begin(), l1.end(),
back_inserter(l2),
bind2nd(less<int>(), 3));
Final Remarks
While writing the code I encountered a dilemma: what should the move_if() algorithm return? On the one hand the algorithm should return an iterator pointing to the new end position of the input container, so the caller can use the erase-remove idiom to shrink the container. But on the other hand the algorithm should return the position of the end of the result container, because otherwise it could be expensive for the caller to find it. In the first solution the result iterator points to this position when the algorithm ends, while in the second solution it is the iterator returned by std::copy() that points to this position. I could return a pair of iterators, but for the sake of making things simple I just return one of the iterators.
std::list<MyClass>::iterator endMatching =
partition(myList.begin(), myList.end(), myCondition);
myOtherList.splice(myOtherList.begin(), myList, endMatching, myList.end());
Note that partition() gives you enough to discriminate matching objects from non matching ones.
(list::splice() is cheap however)
See the following code on a concrete case inspired from
Now to remove elements that match a predicate?
#include <iostream>
#include <iterator>
#include <list>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
list<string> Strings;
Strings.push_back("213");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
// Linear. Exactly last - first applications of pred, and at most (last - first)/2 swaps.
list<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
list<string> NotMatching;
// This function is constant time.
NotMatching.splice(NotMatching.begin(),Strings, Strings.begin(), end1);
cout << "Elements matching with 1" << endl;
copy(Strings.begin(), Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(NotMatching.begin(), NotMatching.end(), ostream_iterator<string>(cout,"\n"));
return 0;
}
Another attempt:
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end; ) {
std::list<MyClass>::iterator eraseiter = it;
++it;
if(myCondition(*eraseiter)) {
myOtherList.push_back(*eraseiter);
myList.erase(eraseiter);
}
}
template <typename ForwardIterator, typename OutputIterator, typename Predicate>
void splice_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Predicate pred)
{
ForwardIterator it = begin;
while( it != end )
{
if( pred(*it) )
{
*begin++ = *out++ = *it;
}
++it;
}
return begin;
}
myList.erase(
splice_if( myList.begin(), myList.end(), back_inserter(myOutputList),
myCondition
),
myList.end()
)