Iterator in std::set containing std::map - c++

How can I can gain access to a map which is stored in std::set? I need to do something like
for (iterator=map.begin(); iterator!=map.end(); iterator++) {
some_function(iterator->first);
}
, but instead of map im using set containing maps.

It's not very different from iterating any other map.
set<map<int, int> > s;
for (set<map<int, int> >::iterator it = s.begin(); it != s.end(); ++it) {
for (map<int, int>::iterator iter = it->begin(); iter != it->end(); ++iter) {
.. do something ...
}
}
So first you iterate over the set and then over the elements of the map pointed to by the outer container's iterator. I have used map<int, int> here just for illustration.

Using range-for makes this much simpler (assuming I understand your question):
for (map<int, int>& m : my_set) {
some_function(m);
}

Related

How can i erase duplicated elements from a multimap<int, std::pair<int, bool>>?

I have a multimap with duplicates. When I finished the collection of the elements i would like to erase the dups.
Here is the container:
std::multimap<int, std::pair<int, bool>> container;
This following code is inside an iteration.(it is a simpler version of the original)
container.emplace(LeafId, std::make_pair(NodeId, isElectronic));
Is it good solution?
std::pair<int, std::pair<int, bool>> lastValue {-1 , {-1, -1}};
for (auto it = container.cbegin(); it != container.cend();)
{
if (it->first == lastValue.first && it->second == lastValue.second)
{
it = container.erase(it);
} else
{
lastValue = *it;
++it;
}
}
Is it good solution?
Unless you keep internal pair sorted inside multimap, no it is not a good solution as it would miss duplicates. If you can change data type then you can use:
std::map<int, std::vector<std::pair<int, bool>>>
instead of std::multimap and then for each element sort vector and remove duplicates using standard algorithms as described here Removing duplicates in a vector of strings
if you cannot I suggest to use additional std::set or std::unordered_set:
std::set<std::pair<int, bool>> tset;
int lastValue = 0;
for (auto it = container.cbegin(); it != container.cend();)
{
if( it->first != lastValue ) {
tset.clear();
lastValue = it->first;
}
if( !tset.insert( it->second ).second )
it = container.erase( it );
else
++it;
}

Get value in a stacked map

I have a stacked map that looks like this:
std::multimap <double, std::map<int,TypeA> > m
How can I point to the value of TypeA? I have tried:
TypeA t = m.second.second
or pointer:
for (std::multimap <double, std::map<int,StopID>>::iterator p = m.begin(); p != m.end(); p ++ ){
....
TypeA t = p->second->second
}
However, I got this error
no member named second in std::map...
Any idea?
for (std::multimap <double, std::map<int,StopID>>::iterator p = m.begin(); p != m.end(); p ++ ){
In the above you are iterating over the multimap and p->second points at the whole inner map, not a single entry in it. You need to iterate over the map too to access all entries in that.
You can use range based for-loops and structured bindings to make life easier.
for(auto& [the_double, the_map] : m) {
for(auto& [the_int, the_type_a] : the_map) {
// do what you want with the TypeA reference "the_type_a"
}
}
Edit: If I understand comments correctly, the map always contains exactly one int, TypeA pair - and in that case, just replace the map with a std::pair<int, TypeA> and you can have one loop only:
#include <utility> // std::pair
int main() {
std::multimap <double, std::pair<int, TypeA>> m;
for(auto& [the_double, the_pair] : m) {
auto& [the_int, the_type_a] = the_pair;
// do what you want with the int and TypeA references
}
}
m->second is a std::map object. You must use a loop for the next map:
using multimap_t = std::multimap <double, std::map<int, TypeA>>;
for (multimap_t::iterator p = m.begin(); p != m.end(); p++) {
std::map<int, TypeA>>& map = p->second;
for (decltype(p->second)::iterator miter = map.begin(); miter != map.end(); ++miter) {
TypeA t = miter->second;
// ...
}
}
A simpler version with auto (C++11):
for (auto p = m.begin(); p != m.end(); p++) {
auto& map = p->second;
for (auto miter = map.begin(); miter != map.end(); ++miter) {
TypeA t = miter->second;
// ...
}
}

How to get all keys from a given 3D map value?

I have a 3D map container declared as follows:
std::map<std::string, std::map<std::string, std::map<std::string, CGridItem*> > > m_3DGridItems;
Suppose I have a CGridItem object pointer value, how can I get all the three map key strings in an efficient way? Thank you!
First of all, do you really need such a clumsy container ?
It would be much easier to have a Key structure:
struct Key {
std::string x;
std::string y;
std::string z;
};
And then define an ordering on Key:
bool operator<(Key const& left, Key const& right) {
if (left.x < right.x) { return true; }
if (left.x > right.x) { return false; }
if (left.y < right.y) { return true; }
if (left.y > right.y) { return false; }
return left.z < right.z;
}
Then you can have a much easier structure to manipulate:
std::map<Key, GridItem*>
If you need to map both ways, check out Boost.Bimap which maintains a two-ways mapping Key <-> GridItem* (so you don't have to sync two structures yourself).
You can just use iterator to get all key/value in a map. When the value is a map too, you can get the key/values the same way ...
First thing: if you are primarily making look-ups like that, this data-structure is definitely not the best performing alternative.
I don't see any other way than to make three nested for loops, since the map is laid out to make look-ups by key and not by value. It would look something like this:
std::map<std::string, std::map<std::string, std::map<std::string, CGridItem*> > >:iterator it1;
CGridItem* obj = ...;
for(it1 = mymap.begin(); it != mymap.end(); ++it1)
{
std::map<std::string, std::map<std::string, CGridItem*> > it2;
for(it2 = it1->second.begin(); it2 != it->second.end(); ++it2)
{
std::map<std::string, CGridItem*> it3;
for(it3 = it2->second.begin(); it3 != it2->second.end(); ++it3)
{
if(it3->second == obj) {
/*found it!*/
/* your 3 strings are in it1->first, it2->first, it3->first */
}
}
}
}
EDIT: I propose the following data structure:
std::map<CGridItem*, std::tuple<std::string, std::string, std::string> > mymap;
This maps your CGridItem object to 3 strings. Note: the std::tuple may not be available when you are not using c++11, but it is available in the boost libraries.

Erasing the last element of a vector by looping through it

I want to loop through a vector and erase certain elements that correspond to a certain criteria, for example:
vector<int> myvector;
vector<int>::iterator it;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
for(it = myvector.begin(); it != myvector.end(); ++it){
if((*it) == 4){
it = myvector.erase(it);
}
}
Now this works fine unless the criterion erases the last item like in the code above. How do you avoid this behaviour ?
Thanks.
EDIT------------------------------------
Now the reason I was looping through it was that there are actually 4 vectors I need to delete the element from (but the criterion is only on one vector):
In this case, is this how to go ?
vector<int> myvector;
vector<int> myvector2;
vector<int> myvector3;
vector<int> myvector4;
vector<int>::iterator it;
vector<int>::iterator it2;
vector<int>::iterator it3;
vector<int>::iterator it4;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
(assume myvector2/3/4 have values inside them)
it2 = myvector2.begin()
it3 = myvector3.begin()
it4 = myvector4.begin()
for(it = myvector.begin(); it != myvector.end();){
if((*it) == 4){
it = myvector.erase(it);
it2 = myvector2.erase(it2);
it3 = myvector3.erase(it3);
it4 = myvector4.erase(it4);
}
else{
++it;
++it2;
++it3;
++it4;
}
}
Is there a modification to the erase/remove idiom valid in this case ?
The usual is the remove/erase idiom, which would look something like this:
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
Edit: Rereading your question, you mention "certain criteria". If the criteria aren't necessarily just removing a single value, you can use std::remove_if instead of std::remove, and specify your criteria in a functor.
Edit2: for the version dealing with four vectors, the usual method is to create a struct holding the four related values, and delete entire structs:
struct x4 {
int a, b, c, d;
// define equality based on the key field:
bool operator==(x4 const &other) { return a == other.a; }
x4(int a_, int b_=0, int c_=0, ind d_=0) : a(a_), b(b_), c(c_), d(d_) {}
};
std::vector<x4> myvector;
myvector.erase(std::remove(myvector.begin(), myvector.end(), x4(4));
Again, if your criteria are more complex than you can easily express in a comparison operator, you can use std::remove_if instead of std::remove. This is also useful if/when you might need to apply different criteria at different times.
If you really need to keep your data in parallel vectors (e.g., you're feeding the data to something external that requires separate, contiguous arrays), then using a loop is probably as good as the alternatives.
Don't do this with a for loop, there's already a well-debugged algorithm for you.
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
I think you should write the loop as :
for(it = myvector.begin(); it != myvector.end(); )
{
if((*it) == 4)
it = myvector.erase(it);
else
++it; //increment here!
}
Because in your code, if you find 4, you update it in the if block itself, but after that you again increment/update it in the for also which is wrong. That is why I moved it to else block that ensures that it gets incremented if you don't find 4 (or whatever value you're searching).
Also remember that erase returns iterator pointing to the new location of the element that followed the last element erased by the function call.
erase is generally used with remove (Also have a look at erase-remove idiom) as shown below
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
for(it = myvector.begin(); it < myvector.end(); ++it){
if((*it) == 4){
it = myvector.erase(it);
}
}
This will make sure your loop will break if the it >= myvector.end().

Removing elements from a C++ map through a for-loop

My STL is a bit rusty, so forgive me for asking a possibly trivial question. Consider the following piece of code:
map<int,int> m;
...
for (auto itr = m.begin(); itr != m.end(); ++itr) {
if (itr->second == 0) {
m.erase(itr);
}
}
The question is: Is it safe to erase elements while looping over the map?
Yes, but not the way you do it. You're invalidating itr when you erase, then incrementing the invalid iterator.
auto itr = m.begin();
while (itr != m.end()) {
if (itr->first == 0) {
m.erase(itr++);
} else {
++itr;
}
}
I think that you shouldn't use removed iterator at all - in case of lists this causes serious problems, shouldn't be different for maps.
EDIT by Matthieu M: this code is well-formed in C++0x and allowed as an extension by MSVC.
map<int,int> m;
...
auto itr = m.begin();
while (itr != m.end())
{
if (itr->second == 0) {
itr = m.erase(itr);
}
else
{
itr++;
}
}
For the example given, It would actually be easier to use the erase overload that takes a key as an argument. This function erases all elements in the map with the given key (for a map, this is always either zero or one element)
map<int,int> m;
// ...
m.erase(0); // erase all elements with key equivalent to 0