Erase nested map elements - c++

I have a map which looks like
typedef std::map<int, std::set<float>> innerMap;
typedef std::map<long, innerMap> outerMap;
I want to do following:
1. I want to erase inner map elements for a key of outer map.
2. Then I want to erase outer map key if it contains 0 inner map elements.
For first scenario, I have written code as:
outerMap mapA;
//assuming this map contain an element
//longx is key in outer element, intx is key of inner element
std::map<int, std::set<float>>::const_iterator innerIter = mapA[longx].begin();
while (innerIter != mapA[longx].end())
{
if (innerIter->first == intx)
{
if (innerIter->second.size() == 0)
{
mapA[longx].erase(innerIter++);
}
break;
}
++innerIter;
}
I have written this code in C++ but I wanna use C++11. Can we write this in a better way in C++11?
For second scenario, do I need to iterate again outer map and check its value elements or I can do it in existing code itself?

This code looks way too complicated to me. The following should do the same, no need to use fancy C++11 features:
outerMap mapA;
// Lookup iterator for element of outerMap.
outerMap::iterator const outerIter = mapA.find(longx);
// Lookup iterator for element of innerMap that should be checked.
innerMap::const_iterator const innerIter = outerIter->second.find(intx);
// Check if element of innerMap should be erased and erase it if yes.
if(innerIter != outerIter->second.end() && innerIter->second.size() == 0) {
outerIter->second.erase(innerIter);
}
// Erase element of outer map is inner map is now empty:
// This should do scenario 2
if(outerIter->second.size() == 0) {
mapA.erase(outerIter);
}

what you do currently (in C++11):
auto& inner = mapA[longx];
const auto it = inner.find(intx);
if (it != inner.end() && it->second.size() == 0) {
inner.erase(it);
}

Related

How can I code a loop that compares every element of an unordered_set with all the others, using iterators in C++?

I have an unordered_set and I need to pick each element and compare it with all the others.
Notes:
If A and B are compared, I don't need to compare B and A.
My unordered_set is the value of an unordered_map, for which the key is a pair.
I tried the following:
unordered_map <pair<int, int>, unordered_set <int>, boost::hash<std::pair<int,int>>> gridMap;
unordered_map <int, rigidBody*> objectsMap;
auto gridMapIt = gridMap.begin();
while (gridMapIt != gridMap.end()) // loop the whole gridMap
{
auto setItOut = gridMapIt->second.begin();
while (setItOut != gridMapIt->second.end()) // loop each element of the set
{
auto setItIn = gridMapIt->second.begin();
while (setItIn != gridMapIt->second.end()) // versus each other element
{
//compare and do stuff
++setItIn;
}
checked.insert({ objectsMap[*setItOut]->getID(), objectsMap[*setItIn]->getID() });
checked.insert({ objectsMap[*setItIn]->getID(), objectsMap[*setItOut]->getID() });
++setItOut;
}
++gridMapIt;
}
The error I am getting is "Expression: cannot dereference end list iterator". If I remove or comment the innermost while loop, it works fine.
Thanks in advance.
The use of *setItIn after the loop is invalid. At that point you have an iterator that points past the last element. That's what the error is telling you.
If you change from while to for you can use the scoping rules to stop yourself from dereferencing invalid iterators.
Rather than populate checked, you can start the inner loop from the next element, rather than the first.
for (auto & gridElem : gridMap) {
for (auto setItOut = gridElem.second.begin(), setEnd = gridElem.second.end(); setItOut != setEnd; ++setItOut) {
for (auto setItIn = std::next(setItOut); setItIn != setEnd; ++setItIn) {
//compare and do stuff
}
// setItIn not visible here
}
}

How can I access the values of a map as a whole

I have a map in c++ like this:
std::map<int, int> points;
I know that I can access the two integers for example in a for loop like this
for (auto map_cntr = points.begin(); map_cntr != points.end(); ++map_cntr)
{
int A = map_cntr->first; // key
int B = map_cntr->second; // val
}
But I want to know how I can access every point as a whole (and not it's entries like above).
I thought something like this:
for (auto map_cntr = points.begin(); map_cntr != points.end(); ++map_cntr)
{
auto whole_point = points.at(map_cntr);
}
Actually, I want to do operations on integers of a entry (point) of the map with integers of the following entry (point) of the map.
I want to do operations on integers of a entry (point) of the map
with integers of the following entry (point) of the map.
Map is not suited container to perform operation depending on the sequence of elements where you want to modify current element according to previous ones. For those things you can use a vector or an array of pairs for instance.
You can use foreach loop
std::map<int, int> points;
for (auto pair : points)
{
// pair - is what you need
pair.second;
pair.first;
auto whole_point = pair;
}
I want to do operations on integers of a entry (point) of the map with integers of the following entry (point) of the map
You can't directly modify the key of a [key,value] pair in a map. If you need to do so, you have to erase the pair and insert another one.
If you only need to write the value of a pair, or if you only need to read the pairs, you can do it with a single iterator, like this:
// assuming the map contains at least 1 element.
auto it = points.begin();
std::pair<const int, int>* currentPoint = &(*it);
it++;
for (; it != points.end(); ++it) {
auto& nextPoint = *it;
// Read-only: currentPoint->first, nextPoint.first
// Read/write: currentPoint->second, nextPoint.second
currentPoint = &nextPoint;
}
Live example

Find and erase element from STL container while iterating

While iterating over a multimap I want to delete elements, but not only the element the iterator is pointing to.
for (vector<int> myVec : myVectors)
{
auto range = myMultiMap.equal_range(myVector);
for (auto it = range.first; it != range.second; ++it)
{
// secondPair is another element of this multimap
auto secondPair = getSecondPair(it, myMultiMap);
if (condition)
{
it = myMultiMap.erase(it);
auto finder = myMultiMap.find(secondPair);
// how can I delete secondPair?
}
}
}
Maybe it's the xy-problem here, so let me explain what I need this for: What I'm trying to do is to shorten a set of vector<int>. There are associated elements of type MyPair for each element. Those associated elements are stored in an unordered multimap.
typedef unordered_multimap < vector<int>, MyPair, SomeHash > MyMultiMap;
An element of the set<vector <int> > can be removed if all associated pairs in the multimap have been processed successfully. It won't be successful for most of them, so most of them are expected to remain in the set. My idea was to delete the elements from the multimap and if there are no associated elements in the multimap anymore, it means the element can be removed from the set.
Here again I have the problem to remove elements from a set while iterating. Again, not only the one the iterator is pointing to.
From cppreference on unordered_multimap::erase:
References and iterators to the erased elements are invalidated. Other iterators and references are not invalidated.
So I think that if you get an iterator to secondPair and secondPairIt != it then you can safely erase secondPairIt. You should also check that you are not invalidating the end of the range.
for (auto it = range.first; it != range.second;)
{
if (condition)
{
auto secondPairIt = getSecondPairIt(it, myMultiMap); // Assume this is not end
if (secondPairIt != it)
{
if (secondPairIt == range.second)
range.second = myMultiMap.erase(secondPairIt);
else
myMultiMap.erase(secondPairIt);
}
it = myMultiMap.erase(it);
}
else
{
++it;
}
}

Is there a .at() equivalent for a multimap?

Is there any way to get an iterator to a multimap, for specific keys? For example:
multimap<string,int> tmp;
tmp.insert(pair<string,int>("Yes", 1));
tmp.insert(pair<string,int>("Yes", 3));
tmp.insert(pair<string,int>("No", 5));
tmp.insert(pair<string,int>("Maybe", 1));
tmp.insert(pair<string,int>("Yes", 2));
multimap<string,int>::iterator it = tmp.at("Yes);
Then I could use it for the work I want to do. Is this possible in C++? Or do we have to just cycle through the multimap, element by element, and check for the key before doing the work?
You have find for a single key value pair (any matching the key), or equal_range to get all of the pairs that match a given key (this seems to be your best bet.)
multimap<Key, T> only sort elements by its Key, so we can only find all the elements whose key value equals "Yes", then check each element one by one.
typedef multimap<string,int>::iterator Iterator;
pair<Iterator, Iterator> iter_range = tmp.equal_range("Yes");
Iterator it;
for (it = iter_range.first; it != iter_range.second; ++it) {
if (it->second == 3) {
break;
}
}
if (it != tmp.end()) {
tmp.erase(it);
}
In fact it's better to use multiset<T> in this case:
multiset< pair<string, int> > temp;
temp.insert(make_pair("Yes", 1));
temp.insert(make_pair("Yes", 3));
multiset< pair<string, int> >::iterator iter = temp.find(make_pair("Yes", 1));
if (iter != temp.end()) {
temp.erase(iter); // it erase at most one element
}
temp.erase(make_pair("Yes", 3)); // it deletes all the elements that equal to make_pair("Yes", 3)

STL Multimap Remove/Erase Values

I have STL Multimap, I want to remove entries from the map which has specific value , I do not want to remove entire key, as that key may be mapping to other values which are required.
any help please.
If I understand correctly these values can appear under any key. If that is the case you'll have to iterate over your multimap and erase specific values.
typedef std::multimap<std::string, int> Multimap;
Multimap data;
for (Multimap::iterator iter = data.begin(); iter != data.end();)
{
// you have to do this because iterators are invalidated
Multimap::iterator erase_iter = iter++;
// removes all even values
if (erase_iter->second % 2 == 0)
data.erase(erase_iter);
}
Since C++11, std::multimap::erase returns an iterator following the last removed element.
So you can rewrite Nikola's answer slightly more cleanly without needing to introduce the local erase_iter variable:
typedef std::multimap<std::string, int> Multimap;
Multimap data;
for (Multimap::iterator iter = data.begin(); iter != data.end();)
{
// removes all even values
if (iter->second % 2 == 0)
iter = data.erase(iter);
else
++iter;
}
(See also answer to this question)