Bagaimana cara menghapus item dari vektor stl dengan nilai tertentu?

145

Saya melihat dokumentasi API untuk vektor stl, dan memperhatikan tidak ada metode pada kelas vektor yang memungkinkan penghapusan elemen dengan nilai tertentu. Ini tampak seperti operasi yang umum, dan tampaknya aneh bahwa tidak ada cara untuk melakukannya.

bradtgmurray
sumber
2
Saya tahu saya telah menyebutkan ini beberapa kali sebelumnya, tetapi buku Scott Meyer, STL Efektif mencakup gotcha ini dengan cara yang jelas.
Rob Wells
Ini mungkin bacaan yang menarik untuk Anda: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

Jawaban:

165

std::removetidak benar-benar menghapus elemen dari wadah, tetapi mengembalikan iterator akhir baru yang dapat diteruskan ke container_type::eraseuntuk melakukan penghapusan NYATA dari elemen tambahan yang sekarang di akhir wadah:

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
sumber
3
Apakah formulir all-in-one-statement ini bergantung pada urutan di mana kompiler mengevaluasi argumen, atau vec.end()dijamin sama di kedua sisi panggilan std::remove? Sepertinya saya membaca bagian lain dari web seperti ini aman, tetapi harus dinyatakan dengan jelas.
dmckee --- ex-moderator kitten
1
Tidak apa-apa. Hasil dari vec.end()tidak harus sama; itu hanya perlu benar (yang itu).
Jim Buck 6/11
8
vec.end()memang harus sama, tapi tidak apa-apa karena std::removetidak mengubahnya. Jika memang mengubahnya (dan membatalkan nilai lama) maka akan ada masalah: urutan evaluasi parameter tidak ditentukan dan karena itu Anda tidak akan tahu apakah yang kedua vec.end()masih valid pada saat itu digunakan. Alasannya sama sederhana, std::removetidak mengubah ukuran wadah, hanya memindahkan isi.
Steve Jessop
2
Saya menemukan pertanyaan ini penting, karena saya memiliki masalah yang sama. Tapi, studio visual saya std::removehanya membawa satu argumen; yang const char *_Filename. Metode apa yang harus saya panggil?
Victor
15
Itu adalah versi removeyang menghapus file. Anda harus memasukkan <algorithm>untuk mengakses versi removeyang berhubungan dengan kontainer.
Jim Buck
64

Jika Anda ingin menghapus sebuah item, berikut ini akan menjadi sedikit lebih efisien.

std::vector<int> v;


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

atau Anda dapat menghindari overhead memindahkan barang jika pesanan tidak penting bagi Anda:

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
sumber
3
Catatan ini tidak akan menghapus duplikat item jika ada, sedangkan pendekatan std :: remove_if tidak.
Den-Jason
15

Gunakan metode global std :: remove dengan iterator begin dan end, dan kemudian gunakan std :: vector.erase untuk benar-benar menghapus elemen.

Tautan dokumentasi
std :: hapus 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

Terima kasih kepada Jim Buck karena menunjukkan kesalahan saya.

bradtgmurray
sumber
Ini memiliki kemampuan untuk mencegah pergerakan elemen-elemen lain pada penghapusan elemen di tengah-tengah vektor sehingga ini lebih cepat. Ini memindahkan mereka ke ujung vektor terlebih dahulu maka Anda bisa membuang dari ujung vektor.
Etherealone
5

Jawaban lain mencakup cara melakukan ini dengan baik, tapi saya pikir saya juga akan menunjukkan bahwa ini tidak benar-benar aneh bahwa ini tidak ada dalam vektor API: itu tidak efisien, pencarian linear melalui vektor untuk nilai, diikuti oleh banyak menyalin untuk menghapusnya.

Jika Anda melakukan operasi ini secara intensif, sebaiknya pertimbangkan std :: set sebagai ganti karena alasan ini.

Luke Halliwell
sumber
5

Jika Anda memiliki vektor yang tidak disortir, maka Anda cukup bertukar dengan elemen vektor terakhir resize().

Dengan wadah memerintahkan, Anda akan pergi terbaik dengan std::vector::erase(). Perhatikan bahwa ada yang std::remove()didefinisikan dalam <algorithm>, tetapi itu tidak benar-benar melakukan penghapusan. (Baca dokumentasi dengan cermat).

nsanders
sumber
3

Dari c ++ 20 :

Fungsi non-anggota diperkenalkan std::erase, yang mengambil vektor dan nilai untuk dihapus sebagai input.

ex:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);
Pavan Chandaka
sumber
2
Tidak hanya itu, kelebihan muatan untuk setiap wadah tertentu!
HolyBlackCat
... dan manfaatkan properti khusus wadah, seperti map::erase!
LF
2

Lihat juga std :: remove_if untuk dapat menggunakan predikat ...

Inilah contoh dari tautan di atas:

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
sumber
2
Silakan tambahkan lebih detail ke posting ini. Seperti berdiri, sebagian besar isinya berasal dari tautan, dan akan hilang jika tautan itu putus.
Mick MacCallum
bagaimana dengan fakta bind2nd adalah - (tidak digunakan lagi dalam C ++ 11) (dihapus dalam C ++ 17)
Idan Banani
0

Jika Anda ingin melakukannya tanpa tambahan termasuk:

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
sumber
0

Ada dua cara yang dapat Anda gunakan untuk menghapus item khususnya. mari kita ambil vektor

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) Cara tidak efisien: Meskipun tampaknya cukup efisien tetapi itu bukan karena fungsi hapus menghapus elemen dan menggeser semua elemen ke kiri oleh 1. sehingga kompleksitasnya adalah 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) Cara efisien (DISARANKAN) : Ini juga dikenal sebagai idiom - HAPUS .

  • std :: remove mengubah rentang yang diberikan menjadi rentang dengan semua elemen yang membandingkan tidak sama dengan elemen yang diberikan bergeser ke awal wadah.
  • Jadi, sebenarnya jangan hapus elemen yang cocok. Itu hanya menggeser yang tidak cocok untuk memulai dan memberikan iterator ke akhir yang valid baru. Itu hanya membutuhkan O (n) kompleksitas.

output dari algoritma hapus adalah:

10 20 30 50 40 50 

sebagai jenis pengembalian hapus adalah iterator ke ujung baru dari rentang itu.

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

Sekarang gunakan fungsi hapus vektor untuk menghapus elemen dari ujung baru ke ujung lama vektor. Membutuhkan O (1) waktu.

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

jadi metode ini bekerja di O (n)

Praveen Kumar
sumber
0

*

Komunitas C ++ telah mendengar permintaan Anda :)

*

C ++ 20 menyediakan cara mudah untuk melakukannya sekarang. Menjadi sesederhana:

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

Anda harus memeriksa std :: erase dan std :: erase_if .

Tidak hanya akan menghapus semua elemen dari nilai (di sini '0'), itu akan melakukannya dalam kompleksitas waktu O (n) . Mana yang terbaik yang bisa Anda dapatkan.

Jika kompiler Anda tidak mendukung C ++ 20, Anda harus menggunakan idiom hapus-hapus :

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