Delete the first N elements from std :: map?

Trying to write a method that removes the first (with the least keys) N elements from std :: map. Tried this:

void EraseNMapElements(const int numElementsToRemove){ const int originalSize = _map.size(); auto eraseIter = _map.begin(); std::advance(eraseIter, numElementsToRemove); _map.erase(_map.begin(), eraseIter); assert(_map.size() == (originalSize - numElementsToRemove)) || (0 == originalSize) || (0 == _map.size())); } 

It works when the number of elements is greater than the number that you want to delete. Therefore, if I had five elements, ask to delete 2, the last element will remain. However, if I have one element and a delete request of 2, I have one element left.

Is there a neat way to cover this? I could force the IF statement around checking for numElementsToRemove more than map.size (), but should there be a better solution?

+7
c ++ c ++ 11 stdmap
source share
4 answers

std::advance(i, n) has the precondition that i can increase at least n times. You do not check for this precondition in your code, so if you call it with numElementsToRemove > originalSize , you violate this precondition and thus experience Undefined Behavior. To fix this, you should do a check before calling std::advance , possibly using std::min :

 auto realNumToRemove = std::min(numElementsToRemove, originalSize); std::advance(eraseIter, realNumToRemove); 
+4
source share

The if provides a simple, readable solution:

 if (originalSize <= numElementsToRemove) { auto eraseIter = _map.begin(); std::advance(eraseIter, numElementsToRemove); _map.erase(_map.begin(), eraseIter); } else { _map.clear(); // or whatever appropriate } 
+4
source share

One thing that hasn't been mentioned yet, and it's good to know, is that C ++ 11 std :: map :: erase (const_iterator) actually returns an iterator to the next element. Thus, the code can also be written as:

 auto i = _map.begin(); while (i != _map.end() && numElementsToRemove > 0) { i = _map.erase(i); --numElementsToRemove; } 

This will cause the items to be erased once instead of two.

+2
source share

The easiest way I can do this is to use std::next and the if statement.

 void EraseNMapElements(const int numElementsToRemove) { if (_map.size() < numElementsToRemove) _map.erase(_map.begin(), std::next(_map.begin(), numElementsToRemove)); else _map.clear(); } 

Note that _map.size() < numElementsToRemove has a sign / unsigned mismatch. Preferably numElementsToRemove should be std::size_t or decltype(_map)::size_type .

+1
source share

All Articles