Let's say that I have vector of pairs, where each pair corresponds to indexes (row and column) of certain matrix I am working on
using namespace std;
vector<pair<int, int>> vec;
I wanted to, using auto, go through the whole vector and delete at once all the pairs that fulfill certain conditions, for example something like
for (auto& x : vec) {
if (x.first == x.second) {
vec.erase(x);
}
}
but it doesn't work, as I suppose vec.erase() should have an iterator as an argument and x is actually a pair that is an element of vector vec, not iterator. I tried to modify it in few ways, but I am not sure how going through container elements with auto exactly works and how can I fix this.
Can I easily modify the code above to make it work and to erase multiple elements of vector, while going through it with auto? Or I should modify my approach?
For now it's just a vector of pairs, but it will be much worse later on, so I would like to use auto for simplicity.
vector::erase() invalidates any outstanding iterators, including the one your range based for loop is using. Use std::remove_if():
vec.erase(
std::remove_if(
vec.begin(),
vec.end(),
[](const pair<int,int> &xx) { return xx.first == xx.second; }
), vec.end()
);
std::remove_if() swaps the elements to the end of the vector and then you can safely erase them.
I would prefer something like this:
pair<int, int> pair = nullptr;
auto iter = vec.begin();
while(iter != vec.end()){
pair = (*iter);
if(pair.first == pair.second){
iter = this->vec.erase(iter);
}else{
++iter;
}
}
Related
I have an std::unordered_map<id, town_data> data, where town_data is a struct of different information - name (string), taxes collected (int) and distance from capital town (int). I'm supposed to build a std::vector<id>, which is sorted by beforementioned distance, lowest to high. I'm quite struggling to figure out how can this be done efficiently. I suppose I could simply loop through the data, create std::map<distance, id> through that loop/insertion, sort it by distance unless maps are sorted by default, and copy it key by key to new vector, std::vector<id>. But this seems really wasteful approach. Am I missing some shortcut or more efficient solution here?
You could create a std::vector of iterators into the map and then sort the iterators according to your sorting criteria. After sorting, you could transform the result into a std::vector<id>.
Create a std::vector of iterators:
std::vector<decltype(data)::iterator> its;
its.reserve(data.size());
for(auto it = data.begin(); it != data.end(); ++it)
its.push_back(it);
Sort that std::vector:
#include <algorithm> // std::sort, std::transform
std::sort(its.begin(), its.end(),
[](auto& lhs, auto&rhs) {
return lhs->second.distance < rhs->second.distance;
});
And finally, transform it into a std::vector<id>:
#include <iterator> // std::back_inserter
std::vector<id> vec;
vec.reserve(its.size());
std::transform(its.begin(), its.end(), std::back_inserter(vec),
[](auto it) {
return it->first;
});
I think that the vector of id can be sorted directly as below,
std::vector<decltype(decltype(data)::value_type::second_type::id)> vec;
vec.reserve(data.size());
for(auto it = data.begin(); it != data.end(); ++it)
vec.push_back(it->second.id);
std::sort(vec.begin(), vec.end(), [&data](auto lhs, auto rhs) { return data[lhs].distance < data[rhs].distance; });
I'm wondering if is this more efficient than sorting a vector of town_data, isn't it?
Since we now have advance() and the prev() to move iterator to go front or go back, and we already have begin() and end().
I wonder is there any situation we better/have to move reverse iterator back and front?
Algorithms often take two iterators that specify a range of elements. For example std::for_each:
std::vector<int> x;
std::for_each(x.begin(),x.end(),foo);
If you want to make for_each iterate in reverse order (note: for_each does iterate in order) then neither advance nor prev are of any help, but you can use reverse iterators:
std::for_each(x.rbegin(),x.rend(),foo);
Because using begin() and end() to iterate in reverse looks horrible:
std::vector<int> v {1, 2, 3};
if(!v.empty()) { //need to make sure of that before we decrement
for(auto it = std::prev(v.end()); ; --it) {
//do something with it
if(it == v.begin()) {
break;
}
}
}
Compare it with reverse iterator version:
std::vector<int> v {1, 2, 3};
for(auto it = v.rbegin(); it != v.rend(); it++) {
//do something with it
}
When you have a function template that takes iterators, and want it to operate on the data in reverse.
E.g.
std::string s = "Hello";
std::string r(s.rbegin(), s.rend());
std::cout << r;
When you use algorithms like std::for_each(), std::accumulate(), std::find_if()... they systematically progress with ++.
If you want this progression to physically occur backwards, then the reverse
iterators are useful.
I guess it is good practise because it seems odd if you start from end and finish in begin. You can easily say last but one by using rbegin.
vector::reverse_iterator itr1;
for (itr1 = vec.rbegin(); itr1 < vec.rend(); itr1++) {
if (*itr1 == num) {
vec.erase((itr1 + 1).base());
}
}
You can use as a function which deletes that Which num want to erase in vector
The need for rbegin()/rend() is because begin() is not the same as rend(), and end() is not rbegin(), see this image from cppreference
This way, you can use any algorithm going forward from beginning to end or backwards from the last to the first element.
There are examples with for each. However, more general, it allows you to reuse any algorithm or operators that works with iterators with advancing, to do the same thing but in a reverse order.
I have a vector which I use for an observer pattern to register the name and pointer: a registration list.
I want to unregister an observers from the vector pair.
I am not sure how to proceed, I tried this but does not compile at all.
vector < pair<string , Observer* > > destination;
void subject::unregisterObservers(LogObserver* obsIfP)
{
vector <pair<string, LogObserverIf*> > ::iterator it;
for(it = observers.begin(); it != observers.end(); it++)
{
if((*it).second == obsIfP)
{
remove(observers.begin(), observers.end(), (*it).first);
break;
}
}
}
How do I remove elements from the vector based on one of the values inside a pair element?
You should use vector::erase() instead.
for(it = observers.begin(); it != observers.end(); it++)
{
if(it->second == obsIfP)
{
observers.erase(it);
break;
}
}
There's a few issues with your current code.
std::remove will find and move elements equal to the given value to the end of the container. It then returns the iterator pointing to the end of the non-removed range in the vector. To have them completely removed would require vector.erase with the iterator returned from the remove algorithm.
The erase-remove idiom:
v.erase( remove( begin(v), end(v), value ), end(v) )
Note, your code gives a string as value and will not compile, since elements in the vector are pair< string, Observer* > and the algorithm can't compare between the two.
Erasing while iterating over the same range is dangerous, since you may invalidate the iterators of the first loop.
Using algorithms with predicates:
Depending on the size of the vector, it may just be simpler (and even faster) to generate a new vector.
typedef pair<string, Observer*> RegPair;
vector<RegPair> new_observers;
new_observers.reserve( observers.size() ); // To avoid resizing during copy.
remove_copy_if( begin(observers), end(obervers), std::back_inserter(new_observers),
[obsIfP]( const RegPair& p ) -> bool
{ return p.second == obsIfP; } );
observers = std::move(new_observers);
// --- OR THIS
observers.erase( remove_if( begin(observers), end(observers),
[obsIfP]( const RegPair& p ) -> bool
{ return p.second == obsIfP; } ),
end(observers) );
Removing an element in the middle of a vector will cause all the following elements to be moved back one index, which is essentially just a copy anyway. If more than one element has the observer pointer, your initial code would have to move these elements more than once, while this suggestion always has a worst case of O(N), where the elementary operation is a copy of the pair element. If better performance than O(N) is required, you'll have to arrange your observers with std::map< string, Observer* > or perhaps boost::bimap which lets you use both pair values as keys.
The difference between remove_if and copy_remove_if would be the preserving of order. remove_if may swap elements and place them elsewhere in order to get the removed element to the end-range. The copy_remove_if will simply not copy it over to the new container, so order is preserved.
If you're not using c++11, the lambda wont work and you'll have to hand code the loop yourself.
I am looping through a vector with a loop such as for(int i = 0; i < vec.size(); i++). Within this loop, I check a condition on the element at that vector index, and if a certain condition is true, I want to delete that element.
How do I delete a vector element while looping over it without crashing?
The idiomatic way to remove all elements from an STL container which satisfy a given predicate is to use the remove-erase idiom. The idea is to move the predicate (that's the function which yields true or false for some element) into a given function, say pred and then:
static bool pred( const std::string &s ) {
// ...
}
std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );
If you insist on using indices, you should not increment the index for every element, but only for those which didn't get removed:
std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
if ( shouldBeRemoved( v[i] ) ) {
v.erase( v.begin() + i );
} else {
++i;
}
}
However, this is not only more code and less idiomatic (read: C++ programmers actually have to look at the code whereas the 'erase & remove' idiom immediately gives some idea what's going on), but also much less efficient because vectors store their elements in one contiguous block of memory, so erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions.
If you cannot use remove/erase (e.g. because you don't want to use lambdas or write a predicate), use the standard idiom for sequence container element removal:
for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
if (delete_condition)
{
it = v.erase(it);
}
else
{
++it;
}
}
If possible, though, prefer remove/erase:
#include <algorithm>
v.erase(std::remove_if(v.begin(), v.end(),
[](T const & x) -> bool { /* decide */ }),
v.end());
Use the Erase-Remove Idiom, using remove_if with a predicate to specify your condition.
if(vector_name.empty() == false) {
for(int i = vector_name.size() - 1; i >= 0; i--)
{
if(condition)
vector_name.erase(vector_name.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
Iterate over the vector backwards. That way, you don't nuke the ability to get to the elements you haven't visited yet.
I realize you are asking specifically about removing from vector, but just wanted to point out that it is costly to remove items from a std::vector since all items after the removed item must be copied to new location. If you are going to remove items from the container you should use a std::list. The std::list::erase(item) method even returns the iterator pointing to the value after the one just erased, so it's easy to use in a for or while loop. Nice thing too with std::list is that iterators pointing to non-erased items remain valid throughout list existence. See for instance the docs at cplusplus.com.
That said, if you have no choice, a trick that can work is simply to create a new empty vector and add items to it from the first vector, then use std::swap(oldVec, newVec), which is very efficient (no copy, just changes internal pointers).
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;
}
}