Suppose you have the incomplete function iterate that has the following parameter, a const map that has pair<int, vector<string>> and the following loops:
string iterate(const map<int, vector<string>>& m) {
for (map<int, vector<string>>::const_iterator start_iter = m.begin(); start_iter != m.end(); ++start_iter) {
for (auto vector_iter = m[(*start_iter).first].begin(); vector_iter != m[(*start_iter).first].end(); ++vector_iter) {
}
}
}
Iterating through a const map requires that the iterator be const, map<int, vector<string>>::const_iterator. That makes sense. However, when trying to iterate through the vector within the constmap what type does auto have to be, or is it not possible to iterate through a container within a const container. I tried making auto vector<string>::const_iterator, but the function still fails. Am I missing something?
Really, do yourself a favor and use range based for loops:
for (auto&& [key, vec] : m) {
for (auto&& vec_element : vec) {
// All vec elements
}
}
If you don't want to use C++17 (you should, C++17 is great!) then do that:
for (auto&& m_element : m) {
for (auto&& vec_element : m_element.second) {
// All vec elements
}
}
If you really want to use use iterators (you really should use range based for loops) then continue reading.
You are not using iterator correctly. You are using a iterator that points on an element in the map, then get it's key to get back the element. This is not how to use an iterator.
Instead, you should use the object the iterator is pointing to directly.
for (auto start_iter = m.begin(); start_iter != m.end(); ++start_iter) {
for (auto vector_iter = start_iter->second.begin(); vector_iter != start_iter->second.end(); ++vector_iter) {
// shuff with vector_iter
}
}
Now why does operator[] failed?
That's because it's a std::map. A with a map, you should be able to create something on the fly:
// creates element at key `123`
m[123] = {"many", "strings", "in", "vector"};
For the element to be created by the container at a new key, it must save that new element in itself, so it must mutate itself to provide operator[], so it's not const.
You could have also use std::map::at(), but it isn't worth it in your case. Using either operator[] or map.at(key) will make the map search for the key, which isn't trivial in complexity.
Related
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;
}
}
I have an std::map and an std::unordered_set with the same key.
I want to remove all keys from the set that do not exist in the map.
My idea was to do something like the following:
#include <map>
#include <unordered_set>
int main()
{
std::map<uint64_t, std::string> myMap = {
{1, "foo"},
{2, "bar"},
{3, "morefoo"},
{4, "morebar"}
};
std::unordered_set<uint64_t> mySet = { 1, 2, 3, 4, 123 };
for (const auto key : mySet)
{
const auto mapIterator = myMap.find(key);
if (myMap.end() == mapIterator)
mySet.erase(key);
}
return 0;
}
And then invoke it on a timer callback every few seconds, however, the snippet above throws an assert when trying to delete the key 123 from mySet, stating:
List iterator is not incrementable.
The thing is, even if it didn't throw an exception I feel like this idea is far from elegant/optimal. I'm wondering if there is a better way to approach this problem?
Thanks.
As stated in answers to this question How to remove from a map while iterating it? you cannot erase elements in container while iterating over it in a for range loop, so your code should use iterator. As such answer already provided I would not duplicate it.
For efficiency you have a quite wrong approach - you are doing lookup in std::map while iterating std::unordered_set, which should be opposite as std::unorederd_set provides faster lookup time (otherwise there is not point for you use it instead of std::set). One of possible approach is to create a copy:
auto copySet = mySet;
for( const auto &p : myMap )
copySet.erase( p.first );
for( const auto v : copySet )
mySet.erase( v );
which could be more efficient, but that depends on your data. Better approach to choose proper data types for your containers.
Note: by wrong approach I mean efficiency only for this particular situation presented in your question, but this seem to be a part of a larger program and this solution can be right for it as there could be more important cases when this data structure works well.
As stated in the comments
for (const auto key : mySet)
{
const auto mapIterator = myMap.find(key);
if (myMap.end() == mapIterator)
mySet.erase(key);
}
will have undefined behavior. When you erase element 123 from mySet you invalidate the iterator that the range based for loop is using. incrementing that iterator is not allowed after you do that. What you can do is switch to a regular for loop so you can control when the iterator is incremented like
for (auto it = mySet.begin(); it != mySet.end();)
{
if (myMap.find(*it) == myMap.end())
it = mySet.erase(it);
else
++it;
}
and now you always have a valid iterator as erase will return the next valid iterator.
In my C++ code I am using a map like this:
std::map<std::pair<int,int>,int*> patterns;
The problem is that I cannot figure out how I get all the keys of that map which are of the form
pair<int,int>
I have seen a few questions related to it, but in all the cases keys are single integers.
If you wanted to just iterate through all the keys:
C++03
for (std::map<std::pair<int,int>,int*>::iterator I = patterns.begin(); I != patterns.end(); I++) {
// I->first is a const reference to a std::pair<int,int> stored in the map
}
C++11
for (auto& kv : patterns) {
// kv.first is a const reference to a std::pair<int,int> stored in the map
}
If you wanted to copy the keys into a new container:
C++03
std::vector<std::pair<int,int> > V;
std::set<std::pair<int,int> > S;
for (std::map<std::pair<int,int>,int*>::iterator I = patterns.begin(); I != patterns.end(); I++) {
V.push_back(I->first);
S.insert(I->first);
}
C++11
std::vector<std::pair<int,int>> V;
std::set<std::pair<int,int>> S;
for (auto& kv : patterns) {
V.push_back(kv.first);
S.insert(kv.first);
}
Because I'm bored, here are a few additional solutions:
You could also do it with standard algorithms and a lambda function, but I don't think this is really better than just writing the loop yourself:
std::vector<std::pair<int,int>> V(patterns.size());
std::transform(patterns.begin(), patterns.end(), V.begin(),
[](decltype(patterns)::value_type& p){ return p.first; });
std::set<std::pair<int,int>> S;
std::for_each(patterns.begin(), patterns.end(),
[&S](decltype(patterns)::value_type& p){ S.insert(p.first); });
You could also use a Boost transform iterator to wrap iterators from the map, such that when the wrapped iterator is dereferenced, it gives you just the key from the map. Then you could call std::vector::insert or std::set::insert directly on a range of transform iterators.
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.
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;
}
}