C++ Erase vector element by value rather than by position? [duplicate]
Asked Answered
S

4

353
vector<int> myVector;

and lets say the values in the vector are this (in this order):

5 9 2 8 0 7

If I wanted to erase the element that contains the value of "8", I think I would do this:

myVector.erase(myVector.begin()+4);

Because that would erase the 4th element. But is there any way to erase an element based off of the value "8"? Like:

myVector.eraseElementWhoseValueIs(8);

Or do I simply just need to iterate through all the vector elements and test their values?

Sew answered 2/8, 2010 at 5:27 Comment(3)
@BenVoigt: your question is quite arrogant - clearly the guy cannot answer it, what YOU should have done is create an answer that covers all the cases you mention.Deontology
@slashmais: Oh nonsense, my clarifying question was quite simple and doesn't require an expert programmer to answer. And there's no way I could cover all possible values of "what do you want to do?" for all three of the cases. Just for the case of "no matching elements" possible behaviors include "nothing", "throw an exception", "return an error", "exit the process (possibly via assert())", "log a message to std::cerr"... and even those aren't exhaustive. No, the asker of the question needs to state the error handling policy, and whether finding no matches even is an error.Briticism
... case of QED. i thinkDeontology
H
571

How about std::remove() instead:

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 8), vec.end());

This combination is also known as the erase-remove idiom.

Highwrought answered 2/8, 2010 at 5:31 Comment(11)
I'm a bit confused... It appears this would erase elements from the vector from the element that I'm looking to erase, all the way to the end of the vector... Am I incorrect? All I want is that one element removed.Sew
@jak: Take a look at the description of remove(): It moves all values not equal to the value passed to the beginning of the range [begin,end). With your example in the question you'd get 5,9,2,0,7,7. As remove() however returns an iterator to the new end, vec.erase() can remove the obsolete elements (i.e. the second 7 here) if that is needed.Highwrought
Is it still necessary to call resize if erase() is used? (If so might consider including it in the answer?)Frankhouse
@Assimilater: It is not necessary.Highwrought
what is the complexity of this procedure? O(n) ???Finland
@Finland The complexities of erase() and remove() are documented, see e.g. on cppreference.Highwrought
@GeorgFritzsche Why can't you just answer his question instead of sending him a link? Yes, it is O(n)Acaulescent
@AlisherKassymov Because the answer is easily found there. And it's a better approach for learning. Different people have different approaches, something worth respecting.Highwrought
The dumb part of this algorithm is that remove doesn't stop when it finds the first match. It is inefficient to keep comparing the rest of the list. So this is great if there are potentially multiple copies of the same value in the vector, but it isn't a good answer for the common scenario of a vector of unique values. In that case, the std::find is a better option.Goethe
Wouldn't it be great if there were "erase_first" and "erase_all" functions on vector? I know vector isn't intended for frequent removals - list, deque or possibly set should be used - but vector is memory efficient so a sensible choice in many situations even when there is some erasing.Perspicuity
I was in doubt about what would happen if remove wouldn't find any element, we would end up with vec.erase(vec.end(),vec.end(). Then looking at the documentation of erase, which says elements in the interval [a,b) are deleted, I thought that the element pointed by the first argument would be definitely deleted, being [ inclusive. What I had not considered is that the interval [a,a) is empty, despite the [Byebye
S
152

You can use std::find to get an iterator to a value:

#include <algorithm>
std::vector<int>::iterator position = std::find(myVector.begin(), myVector.end(), 8);
if (position != myVector.end()) // == myVector.end() means the element was not found
    myVector.erase(position);
Silk answered 2/8, 2010 at 5:31 Comment(10)
This is good if you only expect one occurence of that value.Mitis
@TomášZato: Or want only one removed, which seems to be the case for this question.Atwitter
For removing all the values, initialize position = myVector.begin() and enclose everything in a while(position != myVector.end()) loopAntimonous
Error C2676 binary '==': 'action' does not define this operator or a conversion to a type acceptable to the predefined operator in xutility. Literally any method I come across for this example gives me this error.Audiology
Voted down because this is really not the way you should do this (the most upvoted answer is the correct way).Unbar
@CarloWood it's the correct way if you want to remove a single element.Infallible
@StefanRiedel I don't think that is related. The downside of this method is that if position is not near the end of the vector, then a lot of elements need to be moved. On the "upside", this method keeps all elements in the vector in the same order, but I'd argue that in that case you probably shouldn't be using a vector in the first place. For example if we start with {1, 2, 3, 4, 5, 6, 7} and find and erase 3; this method moves 4, 5, 6 and 7 one place back to get {1, 2, 4, 5, 6, 7}. The accepted answer method instead, swaps the 3 with the 7 and then erases the last element (which is fast!).Unbar
The result in that case would thus be {1, 2, 7, 4, 5, 6} and nothing had to be moved. PS in the previous comment "in that case" should be "if you need that".Unbar
@CarloWood it all depends on the actual needs. OP uses a vector so I assume he needs fast iteration and probably fixed order. I'm not questioning his choice of container, because I can't know what he needs. And based on that, the correct way of deleting a single element is find -> erase. If fixed order is not needed (and again, we don't know) find -> swap -> erase may be better. But in case OP wants to delete all elements meeting that criteria, the accepted answer is the way to go.Infallible
@CarloWood And no, std::remove (and therefor the accepted answer) preserves the order of elements that are not to be removed, so it doesn't swap 3 with 7. Source: en.cppreference.com/w/cpp/algorithm/removeInfallible
F
15

You can not do that directly. You need to use std::remove algorithm to move the element to be erased to the end of the vector and then use erase function. Something like: myVector.erase(std::remove(myVector.begin(), myVector.end(), 8), myVec.end());. See this erasing elements from vector for more details.

Flagellate answered 2/8, 2010 at 5:30 Comment(0)
D
2

Eric Niebler is working on a range-proposal and some of the examples show how to remove certain elements. Removing 8. Does create a new vector.

#include <iostream>
#include <range/v3/all.hpp>

int main(int argc, char const *argv[])
{
    std::vector<int> vi{2,4,6,8,10};
    for (auto& i : vi) {
        std::cout << i << std::endl;
    }
    std::cout << "-----" << std::endl;
    std::vector<int> vim = vi | ranges::view::remove_if([](int i){return i == 8;});
    for (auto& i : vim) {
        std::cout << i << std::endl;
    }
    return 0;
}

outputs

2
4
6
8
10
-----
2
4
6
10

Dogleg answered 2/3, 2016 at 11:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.