How to Delete an Element from a Vector While Looping Over It

C++ how to erase from vector while iterating

erase returns next iterator.

if ((*a) == 2)
{
delete a;
it = intList.erase(it);
}

EDIT:
remove() and remove_if() will copy the elements(pointer here) and one will end up with multiple elements pointing to same integer and if you then try to free them, you'll be left with dangling pointers.

Consider the vector has 4 elements which look something like

0x196c160 0x196bec0 0x196c180 0x196bee0 

One might be tempted to use erase-remove idiom

auto temp = remove_if(vec.begin(),vec.end(),[](const auto &i){return *i==2;});

Now it looks like

0x144aec0 0x144b180 0x144b180 0x144aee0

temp would be pointing to 3rd element and a

for(auto it=temp;it!=vec.end();it++)
delete *it;

Now the second element is a dangling pointer.

EDIT 2:
The above problem could be solved if you delete before the element is copied.Look at @Dietmar's answer.

How to delete an element from a vector while looping over it?

The idiomatic way to remove all elements from an STL container which satisfy a given predicate is to use the remove-erase idiom. The idea is to move the predicate (that's the function which yields true or false for some element) into a given function, say pred and then:

static bool pred( const std::string &s ) {
// ...
}

std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );

If you insist on using indices, you should not increment the index for every element, but only for those which didn't get removed:

std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
if ( shouldBeRemoved( v[i] ) ) {
v.erase( v.begin() + i );
} else {
++i;
}
}

However, this is not only more code and less idiomatic (read: C++ programmers actually have to look at the code whereas the 'erase & remove' idiom immediately gives some idea what's going on), but also much less efficient because vectors store their elements in one contiguous block of memory, so erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions.

Remove element from vector while iterating over it

Solution 1: using shift! and push!

julia> I = Vector{String}(["first", "second", "third", "fourth", "fifth"]);
julia> Inew = Vector{String}(0);
julia> while !isempty(I)
i = shift!(I);
if i == "fourth"; println("Skipped $i");
else println("Got: i = $i"); push!(Inew, i);
end
end
Got: i = first
Got: i = second
Got: i = third
Skipped fourth
Got: i = fifth

julia> show(Inew)
String["first", "second", "third", "fifth"]


Solution 2: Using splice!

julia> I = Vector{String}(["first", "second", "third", "fourth", "fifth"]);
julia> i = 1;
julia> while i <= length(I)
if I[i] == "fourth"; splice!(I,i);
else i += 1;
end
end
julia> show(I)
String["first", "second", "third", "fifth"]

However, note that this is not necessarily more efficient, since new memory is allocated for I whenever you splice it anyway (since it's changing size).


Solution 3: using findin and deleteat! (i.e. "the one-liner"):

julia> I = Vector{String}(["first", "second", "third", "fourth", "fifth"]);
julia> deleteat!(I, findin(I, ["second", "fourth"]))
3-element Array{String,1}:
"first"
"third"
"fifth"

If you don't really need to perform any other intermediate actions (e.g. printing) and you just want to identify and delete elements, then this is probably the way to go.


Further ramblings:


Also, regarding your attempt to do this via a for loop: a cardinal rule when iterating with a for loop (in any language) is that the state of the variable that you're iterating over does not change. Disobeying this rule will usually lead to errors in the best case, or undefined behaviour and silent errors in the worst case. If the state of the variable is meant to change, then you're not looking at a 'for loop' iteration, but at the more general while loop which makes no assumption of consistent state.


I.e. what you've done here is the correct approach and you shouldn't be looking for one that involves a for loop. (and if you do happen to find one, consider that bad code and leave it alone :p ). But, yes, there were nicer-looking ways of doing it; however it's worth noting that what you did is basically re-discover the wheel, in that you made explicit the interface on which the for loop actually relies in julia. I.e. the following code:

for i in MyCollection; print("$i "); end


essentially gets internally translated as its equivalent:

state = start(MyCollection)
while !done( MyCollection, state)
(i, state) = next(MyCollection, state)
print("$i ")
end

How to erase or change element while iterating over vector in C++?

Instead of erasing elements in the middle of the vector, you should write the results from the beginning of the vector and eliminate the unused elements in the end of vector.

int finalSize = 0;
for(int i = 0; i < N; i++)
{
if(result[i] != 0) {
result[finalSize++] = i;
}
}
result.resize(finalSize);

Delete elements from a vector in C++11 while iterating over it

When you call

vec1.erase(itVec1);

you invalidate itVec1. After that, ++itVec1 is not right. It leads to undefined behavior. You need to change your code a little bit.

for( ; itVec1 != vec1.end(); ) {
if (Oracle(*itVec1))
{
itVec1 = vec1.erase(itVec1);
}
else
{
++itVec1;
}
}

You can remove all the boiler plate code by using the Erase-Remove Idiom:

vec1.erase(std::remove_if(vec1.begin(), vec1.end(), Oracle), vec1.end());

Remove elements of a vector inside the loop

You should not increment it in the for loop:

for (vector<Player>::iterator it=allPlayers.begin(); 
it!=allPlayers.end();
/*it++*/) <----------- I commented it.
{

if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}

Notice the commented part;it++ is not needed there, as it is getting incremented in the for-body itself.

As for the error "'operator =' function is unavailable in 'Player’", it comes from the usage of erase() which internally uses operator= to move elements in the vector. In order to use erase(), the objects of class Player must be assignable, which means you need to implement operator= for Player class.

Anyway, you should avoid raw loop1 as much as possible and should prefer to use algorithms instead. In this case, the popular Erase-Remove Idiom can simplify what you're doing.

allPlayers.erase(
std::remove_if(
allPlayers.begin(),
allPlayers.end(),
[](Player const & p) { return p.getpMoney() <= 0; }
),
allPlayers.end()
);

1. It's one of the best talks by Sean Parent that I've ever watched.

Removing item from vector, while in C++11 range 'for' loop?

No, you can't. Range-based for is for when you need to access each element of a container once.

You should use the normal for loop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.

For example:

auto i = std::begin(inv);

while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}


Related Topics



Leave a reply



Submit