Erase final member of std::set - c++

How can I delete the last member from a set?
For example:
set<int> setInt;
setInt.insert(1);
setInt.insert(4);
setInt.insert(3);
setInt.insert(2);
How can I delete 4 from setInt? I tried something like:
setInt.erase(setInt.rbegin());
but I received an error.

in C++11
setInt.erase(std::prev(setInt.end()));
You can decide how you want to handle cases where the set is empty.

if (!setInt.empty()) {
std::set<int>::iterator it = setInt.end();
--it;
setInt.erase(it);
}
By the way, if you're doing this a lot (adding things to a set in arbitrary order and then removing the top element), you could also take a look at std::priority_queue, see whether that suits your usage.

Edit: You should use std::prev as shown in Benjamin's better answer instead of the older style suggested in this answer.
I'd propose using a different name for rbegin which has a proper type:
setInt.erase(--setInt.end());
Assuming you checked that setInt is not empty!
Btw. this works because you can call the mutating decrement operator on a temporary (of type std::set<int>::iterator). This temporary will then be passed to the erase function.

A bit less performant, but an alternative option:
setInt.erase(*setInt.rbegin());

If you want to delete 4 instead of the last you should use the find method.
Depending on the use case 4 might not be the last.
std::set<int>::iterator it = setInt.find(4);
if(it != setInt.end()) {
setInt.erase(it);
}
If you want to delete the last element use:
if (!setInt.empty()) {
setInt.erase(--setInt.rbegin().base());
// line above is equal to
// setInt.erase(--setInt.end());
}
While I was not sure if --*.end(); is O.K. I did some reading.
So the -- on rbegin().base() leads to the same result as -- on end().
And both should work.

Check if the set is empty or not. If not, then get the last element and set that as iterator and reduce that iterator and erase the last element.
if (!setInt.empty())
{
std::set<int>::iterator it = setInt.end();
--it;
if(it != setInt.end()) {
setInt.erase(it);
}
}

Related

Erase an element from a map then put it back

Suppose I have a backtracking algorithm where I need to remove an element from a map, do something, then put it back. I am not sure if there is a good way to do it:
func(std::<K, V> map &dict) {
for (auto i : dict) {
remove i from dict;
func(dict);
put i back to dict;
}
}
I know there are ways to delete an element from map in here but I am not sure if there are ways to achieve what I described above. I am aware that I can create a new dict in for loop and pass it in func but I am wondering if it can be done using the same dict.
What you ask is definitely possible. Here is one way to do it while trying to keep things simple and efficient:
void func(std::map<K, V> &dict) {
for (auto i = dict.cbegin(); i != dict.cend(); ) {
auto old = *i;
i = dict.erase(i); // i now points to the next element (or dict.end())
some_other_func(dict);
dict.insert(i, old); // i is used a hint where to re-insert the old value
}
}
By calling std::map::erase with an iterator argument and std::map::insert with a hint, the complexity of each iteration through this loop is amortized constant. Note that I assumed your calling of func in line 5 was actually supposed to be some_other_func, because calling func recursively would invalidate the iterator you carefully set in line 4.
However, this is not the most efficient way to do this sort of processing (#rakurai has a suggestion that should be considered).
This question has an answer here.
However, your problem may be that you would like the function to ignore the key K while processing dict. How about a second parameter "K ignore" and make the function skip that pair? You could give it a default value in the declaration if that breaks other code, or overload the function.

Erasing item in a for(-each) auto loop

Is there a way to erase specific elements when using a auto variable in a for loop like this?
for(auto a: m_Connections)
{
if(something)
{
//Erase this element
}
}
I know I can either do say
for(auto it=m_map.begin() ...
or
for(map<int,int>::iterator it=m_map.begin() ...
and manually increment the iterator (and erase) but if I could do it with less lines of code I'd be happier.
Thanks!
You can't. A range-based loop makes a simple iteration over a range simpler, but doesn't support anything that invalidates either the range, or the iterator it uses. Of course, even if that were supported, you couldn't efficiently erase an element without access to the iterator.
You'll need an old-school loop, along the lines of
for (auto it = container.begin(); it != container.end();) {
if (something) {
it = container.erase(it);
} else {
++it;
}
}
or a combination of container.erase() and std::remove_if, if you like that sort of thing.
No, there isn't. Range based for loop is used to access each element of a container once.
Every time an element is removed from the container, iterators at or after the erased element are no longer valid (and given the implementation of the range-based-for this is a problem).
You should use the normal for loop (or a while) if you need to modify the container as you go along.
If you want to erase elements for which a predicate returns true, a good way is:
m_Connections.erase(
std::remove_if(m_Connections.begin(),
m_Connections.end(),
[](Type elem) { return predicate(elem); }),
m_Connections.end());
std::remove_if doesn't mix iteration logic with the predicate.
You need the iterator if you want to erase an element from a container.
And you can't get the iterator from the element itself -- and even if you could, for instance with vector, the iterator that range-based for internally uses would be invalidated in the next step causing undefined behavior.
So the answer is: No, in its classic usage you can't. range-based for was solely designed for convenient iteration of all elements in a range.
push all elements into array and then do pop operation to remove the item

c++: popping an element by key out of an std::map

I'm interested in removing an element with a specific key out of a map and use this element.
Something that will look like:
itr = MyMap.pop(wantedKey);
//Now MyMap is missing the element which has the key 'wantedKey'.
//Do something with this element through 'itr'.
Is there an stl map method for doing this?
EDIT
Following carleeto's response, I want to clarify: What I need is the element being removed from the map and the program being able to use it afterwards, it could be the element itself as a pair, not necessarily an iterator.
There are two options: use it in-place then remove it, or move it to a local variable, remove the entry, then use it.
// use-remove
auto i = MyMap.find(wantedKey);
if (i != MyMap.end()) {
// use-remove
use(i->second);
MyMap.erase(i);
// or
// move-remove-use
auto x = std::move(i->second);
MyMap.erase(i);
use(x);
} else {
// Not found
}
Not that I know of, but you can use std::map::find to get an iterator and then call std::map::erase with said iterator as an argument when you're done.
From your variable naming, I think you might be confusing concepts here.
itr = MyMap.pop(wantedKey);
//Do something with this element through 'itr'.
Iterators only point to elements in containers. Therefore, if you had received an iterator through a function called pop (even if it existed), the iterator would reference not the element you popped, but probably the one after or before it, like std::vector::erase. This is because the purpose of an iterator is to iterate over the elements in a container. Therefore, if an element is not in the container, you cannot get an iterator to it. However, even if you used the iterator returned by the erase function, it would not reference you would be expecting it to.
So you can erase an element from the map, like so many have pointed out, by searching for it, getting the ierator to it and then calling erase with that iterator. but you cannot get an iterator that points to element you have erased. Hope this clears things up.
UPDATE: If all you want is to access the element and use it, then all you need to do use std::map::find to get an iterator and std::map::erase to remove the item from the map, once you have finished using the iterator. The reason is that even if you have stored a copy of the iterator for future use, once you call erase, it will be invalidated. To be able to access it after you have erased it, depending on scope, you will probably need to copy it.
Finally, what you want to do is a very common task - look up a map based on a key and perform an operation on the associated element. It's quite likely that you have a list of keys to go through. You should also look up functors, std::for_each and std::transform. I realise this is not operating on the element after you have removed it, but I thought I would add it in, seeing as how its a related operation. For example: You could move all elements that match a list of keys into another container (say, a vector, and then use the above to operate on them).
Probably what you want to do is
itr = MyMap.find('thing in a string');
to find the iterator and then use it,
MyMap.erase(itr)
And then erase it.
Pop() belongs to the stack datastructure. To access an element of a map, use the [] operator (http://www.cplusplus.com/reference/map/map/operator%5B%5D/), to remove it from the map use (http://www.cplusplus.com/reference/map/map/erase/).
itr = MyMap.find(wantedKey);
if(itr != MyMap.end()) {
use( itr->second );
MyMap.erase(itr);
}
your.uncle = bob;
Using C++'s std::map<T, U>::find():
map.erase(map.find(key));
The way I did it is below. In my case the map stores std::shared_ptr values, making the copy cheap(ish), and the object ownership transferral clear.
auto it = MyMap.find( wantedkey );
if ( it == MyMap.end() ) throw runtime_error("not found");
auto ret = it->second; // make copy of shared_ptr
MyMap.erase(it);
return ret;
The caller gets a shared_ptr with a reference count of at least one (from the copy). Note the function must return the shared_ptr by value, etc.

c++ stl vector iterator insert segmentation fault

std::vector<struct::event>::iterator it;
std::vector<struct::event>::iterator last=myvector.end();
for (it=myvector.begin(); it<=last; it++){
if(mysignal.declination<(*last).declination){
if (mysignal.declination>=(*it).declination && mysignal.declination<(*(it+1)).declination){
myvector.insert(it+1, mysignal);
break;
}
}
if (mysignal.declination>=(*last).declination){
myvector.push_back(mysignal);
break;
}
}
I have a vector called myvector with events that are sorted with the declination. now I want to add mysignal to this vector on the right place. but i always get a seg fault after a few events which refers to: if(mysignal.declination<(*last).declination). I just can't see what is wrong.
Your loop is wrong, read the docs:
Returns an iterator to the element following the last element of the container.
This element acts as a placeholder; attempting to access it results in undefined behavior.
You can't dereference end(), it provides a way of knowing that you have overrun the container, so your loop condition should be it != myvector.end(), and last is wrong as well.
As others have said, C++ iterators define a half-open interval
('[begin()...end())'), which is what you should probably be
using in most other cases as well. And although it works with
iterators from a vector, in general, iterators do not support
<= (nor <); the standard idiom for a loop is:
for ( auto current = container.begin();
current != container.end();
++ current ) ...
(In the most likely case that you cannot count on C++11, you'll
have to write out the full iterator type, rather than use
auto. Although auto is one of the few things from C++11
that seems to work with VC++11 and with recent versions of
g++, so if those are the only targets you're concerned with, and
you can be sure of always having very recent versions, you can
use it.)
Also, if you want to access the last element of the vector in
the loop, myvector.back() will return a reference to it.
(myvector.back() is undefined behavior if the vector is empty,
but if the vector is empty, you won't enter the loop.)
end() does not refer to the last element in the container, you need to change your condition as follows.
for (it=myvector.begin(); it != last; it++){
You have other broken logic as well that is dereferencing last that you need to fix.

Remove elements from list in a loop without break

In the code of my game, I want to remove some elements from a list,
which happens in a loop.The only problem I have is, when I use
list::erase I have to break after that function because I think
the list becomes "outdated". This causes a little flicker and I want to
try to remove it.
The current code is this:
for(list<Arrow*>::iterator it = arrows.begin(); it != arrows.end(); it++)
{
Arrow* a = (*it);
if(a->isActive() == true)
{
a->update();
}
else
{
arrows.erase(it);
break;
}
}
Thank you in advance!
Edit:
Sorry, I was confused with vector and list. Got the answer, thanks!
you should do:
it = arrows.erase(it);
//
list<Arrow*>::iterator it = arrows.begin();
while (it != arrows.end())
{
Arrow* a = (*it);
if(a->isActive())
{
a->update(); ++it;
}
else{ // delete (a); ???
it=arrows.erase(it);}
}
I am confused, you say vector and in your example you are using a list.
List is implemented with a double linked-list (actually it is likely to be implemented, because the standard fix just the complexity and not the details). The iterator after erasing are still valid. http://www.cplusplus.com/reference/stl/list/erase/
Erasing in the middle with vector is slow and also invalided all the iterators and references.
Like everyone said you example is confusing. If you want delete in the mid of your container and if you are using
1>vector:
Then every iterator and reference after the point of erase is invalidated.
Also vector deleting from mid of vector will causing element behind it to shift which might considered slow if you want performance.
2>list:
Then only the deleted iterator and reference is invalidated.
Either way using the erase-remove idiom is preferred when you want to delete some element in the middle of stl sequence container.
The proper way to filter out items in a standard library container is to use the Erase-Remove Idiom. Because you're using a member function as the test in the loop, you should adapt the example to use std::remove_if().
You can implement this in a short and sweet (provided you like functional programming) way:
#include <algorithm>
#include <functional>
arrows.erase(std::remove_if(
arrows.begin(), arrows.end(), std::mem_fun(&Arrow::isActive)
));
This will work for std::vector<>, std::deque<>, std::list<> etc. regardless of implementation and iterator invalidation semantics.
Edit: I see you're also using the Arrow::update() method inside the loop. You can either do a double pass on the list, use a function object to call both methods or write the loop manually.
In the last case, you can use the it = arrows.erase(it); trick, but this will only be efficient for std::list<>. The loop will have O(n^2) complexity for the
std::vector<> and std::deque<> containers.