Como faço para remover um item de um vetor stl com um determinado valor?

145

Eu estava olhando a documentação da API para o vetor stl e notei que não havia um método na classe vetorial que permitisse a remoção de um elemento com um determinado valor. Isso parece uma operação comum e parece estranho que não haja uma maneira integrada de fazer isso.

bradtgmurray
fonte
2
Eu sei que já mencionei isso várias vezes antes, mas o livro de Scott Meyer, Effective STL, cobre essas dicas de uma maneira clara.
Rob Wells
1
Relacionados: stackoverflow.com/questions/3385229/...
bobobobo
Esta pode ser uma leitura interessante para você: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

Respostas:

165

std::removena verdade, não apaga o elemento do contêiner, mas retorna o novo iterador final que pode ser passado para container_type::erasea remoção REAL dos elementos extras que estão agora no final do contêiner:

std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());
Jim Buck
fonte
3
Esse formulário da instrução tudo-em-um depende da ordem em que o compilador avalia os argumentos ou é vec.end()garantido que seja o mesmo nos dois lados da chamada std::remove? Parece-me que a leitura de outras partes da Web é segura, mas deve ser declarada claramente.
dmckee --- gatinho ex-moderador
1
Está bem. O resultado de vec.end()não precisa ser o mesmo; só precisa estar correto (qual é).
Jim Buck
8
vec.end()precisa ser o mesmo, mas tudo bem, porque std::removenão muda. Se o alterasse (e invalidasse o valor antigo), haveria um problema: a ordem de avaliação dos parâmetros não é especificada e, portanto, você não saberia se o segundo vec.end()ainda é válido no momento em que é usado. O motivo é o mesmo: é simples, std::removenão altera o tamanho do contêiner, apenas move o conteúdo.
Steve Jessop
2
Eu achei essa pergunta importante, pois tenho o mesmo problema. Mas, meu estúdio visual usa std::removeapenas um argumento; que é const char *_Filename. Qual método eu preciso chamar?
246 Victor Victor
15
Essa é a versão removeque exclui um arquivo. Você precisa incluir <algorithm>para acessar a versão removeque lida com contêineres.
Jim Buck
64

Se você deseja remover um item, o seguinte será um pouco mais eficiente.

std::vector<int> v;


auto it = std::find(v.begin(), v.end(), 5);
if(it != v.end())
    v.erase(it);

ou você pode evitar a sobrecarga de mover os itens se o pedido não for importante para você:

std::vector<int> v;

auto it = std::find(v.begin(), v.end(), 5);

if (it != v.end()) {
  using std::swap;

  // swap the one to be removed with the last element
  // and remove the item at the end of the container
  // to prevent moving all items after '5' by one
  swap(*it, v.back());
  v.pop_back();
}
Etherealone
fonte
3
Observe que isso não removerá duplicatas do item se elas existirem, enquanto a abordagem std :: remove_if o faz.
Den-Jason
15

Use o método global std :: remove com o iterador de início e fim e, em seguida, use std :: vector.erase para remover os elementos.

Links da documentação
std :: remove http://www.cppreference.com/cppalgorithm/remove.html
std :: vector.erase http://www.cppreference.com/cppvector/erase.html

std::vector<int> v;
v.push_back(1);
v.push_back(2);

//Vector should contain the elements 1, 2

//Find new end iterator
std::vector<int>::iterator newEnd = std::remove(v.begin(), v.end(), 1);

//Erase the "removed" elements.
v.erase(newEnd, v.end());

//Vector should now only contain 2

Agradecemos a Jim Buck por apontar meu erro.

bradtgmurray
fonte
Isso tem a capacidade de impedir o movimento de outros elementos ao apagar um elemento no meio do vetor, portanto, isso é mais rápido. Ele os move para o final do vetor primeiro e você pode descartá-los do final do vetor.
Etherealone
5

As outras respostas abrangem como fazer isso bem, mas também gostaria de salientar que não é realmente estranho que isso não esteja na API do vetor: é uma pesquisa ineficiente e linear pelo valor do vetor, seguida por um monte de copiar para removê-lo.

Se você estiver fazendo essa operação intensivamente, pode valer a pena considerar std :: set por esse motivo.

Luke Halliwell
fonte
5

Se você tiver um vetor não classificado, poderá simplesmente trocar com o último elemento vetorial resize().

Com um recipiente ordenou, você será melhor fora com std::vector::erase(). Observe que existe um std::remove()definido em <algorithm>, mas que na verdade não faz a exclusão. (Leia a documentação cuidadosamente).

nsanders
fonte
3

A partir do c ++ 20 :

Uma função não membro introduzida std::erase, que leva o vetor e o valor a serem removidos como entradas.

ex:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);
Pavan Chandaka
fonte
2
Além disso, ele está sobrecarregado para cada contêiner específico!
21919 HolyBlackCat
... e tira proveito das propriedades específicas do contêiner, como map::erase!
LF
2

Veja também std :: remove_if para poder usar um predicado ...

Aqui está o exemplo do link acima:

vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(2);
V.push_back(8);
V.push_back(5);
V.push_back(7);

copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
    // The output is "1 4 2 8 5 7"

vector<int>::iterator new_end = 
    remove_if(V.begin(), V.end(), 
              compose1(bind2nd(equal_to<int>(), 0),
                       bind2nd(modulus<int>(), 2)));
V.erase(new_end, V.end()); [1]

copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
    // The output is "1 5 7".
Xavier Nodet
fonte
2
Adicione mais detalhes a este post. Tal como está, a maioria do conteúdo vem de um link e seria perdida se o link quebrar algum dia.
Mick MacCallum
o que acontece com o fato de bind2nd é - (descontinuado em C ++ 11) (removido em C ++ 17) #
Idan Banani
0

Se você quiser fazer isso sem nenhum extra, inclua:

vector<IComponent*> myComponents; //assume it has items in it already.
void RemoveComponent(IComponent* componentToRemove)
{
    IComponent* juggler;

    if (componentToRemove != NULL)
    {
        for (int currComponentIndex = 0; currComponentIndex < myComponents.size(); currComponentIndex++)
        {
            if (componentToRemove == myComponents[currComponentIndex])
            {
                //Since we don't care about order, swap with the last element, then delete it.
                juggler = myComponents[currComponentIndex];
                myComponents[currComponentIndex] = myComponents[myComponents.size() - 1];
                myComponents[myComponents.size() - 1] = juggler;

                //Remove it from memory and let the vector know too.
                myComponents.pop_back();
                delete juggler;
            }
        }
    }
}
Katianie
fonte
0

Existem duas maneiras pelas quais você pode usar para apagar um item particularmente. vamos pegar um vetor

std :: vector < int > v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(40);
v.push_back(50);

1) Maneira não eficiente: embora pareça ser bastante eficiente, mas não é porque a função apagar apaga os elementos e muda todos os elementos para a esquerda por 1., de modo que sua complexidade será O (n ^ 2)

std :: vector < int > :: iterator itr = v.begin();
int value = 40;
while ( itr != v.end() )
{
   if(*itr == value)
   { 
      v.erase(itr);
   }
   else
       ++itr;
}

2) Maneira eficiente (RECOMENDADO) : Também é conhecido como expressões ERASE - REMOVE .

  • std :: remove transforma o intervalo fornecido em um intervalo com todos os elementos comparáveis ​​diferentes de um determinado elemento deslocados para o início do contêiner.
  • Portanto, na verdade, não remova os elementos correspondentes. Apenas mudou o não correspondente para inicial e fornece um iterador para um novo final válido. Requer apenas O (n) complexidade.

A saída do algoritmo de remoção é:

10 20 30 50 40 50 

como o tipo de retorno de remoção é o iterador para o novo final desse intervalo.

template <class ForwardIterator, class T>
  ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);

Agora use a função de exclusão do vetor para excluir elementos do novo extremo para o antigo do vetor. Requer tempo O (1).

v.erase ( std :: remove (v.begin() , v.end() , element ) , v.end () );

então esse método funciona em O (n)

Praveen Kumar
fonte
0

*

A comunidade C ++ ouviu sua solicitação :)

*

O C ++ 20 oferece uma maneira fácil de fazer isso agora. Fica tão simples quanto:

#include <vector>
...
vector<int> cnt{5, 0, 2, 8, 0, 7};
std::erase(cnt, 0);

Você deve verificar std :: erase e std :: erase_if .

Não apenas removerá todos os elementos do valor (aqui '0'), mas também o fará com complexidade de tempo O (n) . Qual é o melhor que você pode obter.

Se o seu compilador não suportar C ++ 20, você deverá usar o idioma erase-remove :

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
Harshad Sharma
fonte