मैं एक निश्चित मान के साथ एक stl वेक्टर से एक आइटम कैसे निकालूं?


145

मैं stl वेक्टर के लिए एपीआई प्रलेखन को देख रहा था, और देखा कि वेक्टर वर्ग पर कोई विधि नहीं थी जो एक निश्चित मूल्य के साथ एक तत्व को हटाने की अनुमति देता था। यह एक आम ऑपरेशन की तरह लगता है, और यह अजीब लगता है कि ऐसा करने का कोई तरीका नहीं है।


2
मुझे पता है कि मैंने पहले भी कई बार इसका उल्लेख किया है, लेकिन स्कॉट मेयर की पुस्तक इफेक्टिव एसटीएल इन गोचरों को स्पष्ट रूप से कवर करती है।
रॉब वेल्स


यह आपके लिए एक दिलचस्प रीडिंग हो सकता है: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

जवाबों:


165

std::removeवास्तव में कंटेनर से तत्व को नहीं मिटाता है, लेकिन यह नए सिरे के पुनरावृत्त को लौटाता है जिसे कंटेनर के अंत में होने container_type::eraseवाले अतिरिक्त तत्वों के वास्तविक निष्कासन को करने के लिए पारित किया जा सकता है :

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());

3
क्या यह ऑल-इन-वन-स्टेटमेंट फॉर्म उस आदेश पर निर्भर करता है जिसमें कंपाइलर तर्कों का मूल्यांकन करता है, या vec.end()कॉल के दोनों ओर समान होने की गारंटी है std::remove? यह मुझे वेब के अन्य हिस्सों को पढ़ने में लगता है जैसे यह सुरक्षित है, लेकिन इसे स्पष्ट रूप से कहा जाना चाहिए।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

1
यह ठीक है। परिणाम के vec.end()समान होने की आवश्यकता नहीं है; यह सिर्फ सही होना चाहिए (जो यह है)।
जिम बक

8
vec.end()समान होने की आवश्यकता है, लेकिन यह ठीक है क्योंकि std::removeइसे नहीं बदलता है। यदि उसने इसे बदल दिया (और पुराने मूल्य को अमान्य कर दिया) तो एक समस्या होगी: मापदंडों के मूल्यांकन का क्रम अनिर्दिष्ट है और इसलिए आपको यह नहीं पता होगा कि यह उपयोग किए जाने तक दूसरा vec.end()अभी भी मान्य है या नहीं । इसका कारण वही है जो सरल है, std::removeकंटेनर का आकार नहीं बदलता है, यह सिर्फ सामग्री को इधर-उधर करता है।
स्टीव जेसप

2
मुझे यह प्रश्न महत्वपूर्ण लगा है, क्योंकि मुझे भी यही समस्या है। लेकिन, मेरा दृश्य स्टूडियो std::removeकेवल एक तर्क के साथ लेता है; वह यह है कि const char *_Filename। मुझे किस विधि से कॉल करने की आवश्यकता है?
विक्टर

15
यह उस removeफ़ाइल का संस्करण है जो किसी फ़ाइल को हटा देता है। कंटेनर के साथ उस सौदे <algorithm>के संस्करण का उपयोग करने के लिए आपको शामिल करने की आवश्यकता है remove
जिम बक

64

यदि आप किसी आइटम को निकालना चाहते हैं , तो निम्नलिखित कुछ अधिक कुशल होगा।

std::vector<int> v;


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

या आप आइटम को स्थानांतरित करने से बच सकते हैं यदि ऑर्डर आपके लिए मायने नहीं रखता है:

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();
}

3
ध्यान दें कि यदि वे मौजूद हैं तो आइटम के डुप्लिकेट को नहीं हटाया जाएगा, जबकि std :: remove_if दृष्टिकोण करता है।
डेन-जेसन

15

वैश्विक विधि एसटीडी का प्रयोग करें: शुरुआत और अंत के साथ हटानेवाला, और उसके बाद एसटीडी का उपयोग करें: वेक्टर। वास्तव में तत्वों को हटाने के लिए।

दस्तावेज़ लिंक
एसटीडी :: http://www.cppreference.com/cppalgorithm/remove.html
std :: वेक्टर . 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

मेरी त्रुटि को इंगित करने के लिए जिम बक का धन्यवाद।


यह वेक्टर के बीच में एक तत्व के क्षरण पर अन्य तत्वों की आवाजाही को रोकने की क्षमता है इसलिए यह तेज है। यह उन्हें पहले वेक्टर के अंत में ले जाता है, फिर आप वेक्टर के अंत से हट सकते हैं।
ईथरेलोन

5

अन्य उत्तर इसे अच्छी तरह से करने के लिए कवर करते हैं, लेकिन मैंने सोचा कि मैं यह भी बताऊंगा कि यह वास्तव में अजीब नहीं है कि यह वेक्टर एपीआई में नहीं है: यह अकुशल है, मान के लिए वेक्टर के माध्यम से रैखिक खोज, इसके बाद एक गुच्छा। इसे हटाने के लिए नकल की।

यदि आप इस ऑपरेशन को गहनता से कर रहे हैं, तो यह इस कारण के बजाय std :: सेट पर विचार करने के लायक हो सकता है।


5

यदि आपके पास एक अनसुलझा वेक्टर है, तो आप बस अंतिम वेक्टर तत्व के साथ स्वैप कर सकते हैं resize()

एक आदेश दिया कंटेनर के साथ, आप के साथ सबसे अच्छा बंद हो जाएगा std::vector::erase()। ध्यान दें कि वहाँ एक std::remove()परिभाषित है <algorithm>, लेकिन यह वास्तव में मिटा नहीं है। (ध्यान से प्रलेखन पढ़ें)।


3

एक छोटा समाधान (जो आपको वेक्टर नाम को 4 बार दोहराने के लिए मजबूर नहीं करता है) बूस्ट का उपयोग करना होगा:

#include <boost/range/algorithm_ext/erase.hpp>

// ...

boost::remove_erase(vec, int_to_remove);

Http://www.boost.org/doc/libs/1_64_0/libs/range/doc/html/range/reference/algorithms/new/remove_erase.html देखें


3

से c ++ 20 :

एक गैर-सदस्यीय कार्य शुरू किया गया std::erase , जो इनपुट के रूप में हटाए जाने वाले वेक्टर और मूल्य को लेता है।

उदाहरण के लिए:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);

2
इतना ही नहीं, यह प्रत्येक विशिष्ट कंटेनर के लिए अतिभारित है!
होलीब्लैककैट

... और कंटेनर-विशिष्ट गुणों का लाभ उठाता है, जैसे map::erase!
एलएफ

2

स्टड भी देखें :: remove_if एक विधेय का उपयोग करने में सक्षम होने के लिए ...

यहाँ ऊपर दिए गए लिंक से उदाहरण दिया गया है:

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".

2
कृपया इस पोस्ट में और अधिक विवरण जोड़ें। जैसा कि यह खड़ा है, इसकी अधिकांश सामग्री एक लिंक से आती है, और यदि लिंक कभी टूट जाता है तो यह खो जाएगा।
मिक मैक्लम

इस तथ्य के बारे में bind2nd क्या है - (C ++ 11 में पदावनत) (C ++ 17 में हटा दिया गया)
Idan Banani

0

यदि आप इसे बिना किसी अतिरिक्त के करना चाहते हैं:

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;
            }
        }
    }
}

0

दो तरीके हैं जिनके द्वारा आप किसी आइटम को विशेष रूप से मिटाने के लिए उपयोग कर सकते हैं। चलो एक वेक्टर ले लो

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) गैर कुशल तरीका: हालांकि यह काफी कुशल प्रतीत होता है, लेकिन यह नहीं है क्योंकि फ़ंक्शन को मिटाता है तत्वों को छोड़ देता है और सभी तत्वों को बाईं ओर 1 से स्थानांतरित कर देता है। इसलिए इसकी जटिलता ओ होगी (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) कुशल तरीका (सिफारिश) : इसे ERASE - REMOVE मुहावरों के रूप में भी जाना जाता है ।

  • std :: remove दिए गए रेंज को सभी तत्वों के साथ एक सीमा में बदल देता है, जो कंटेनर के प्रारंभ में स्थानांतरित किए गए दिए गए तत्व के बराबर नहीं है।
  • तो, वास्तव में मिलान तत्वों को हटा नहीं है। यह सिर्फ शुरू करने के लिए मिलान किए गए गैर-मिलान में स्थानांतरित हो गया और नए वैध अंत के लिए एक पुनरावृत्ति देता है। इसके लिए केवल O (n) जटिलता की आवश्यकता है।

निकालें एल्गोरिदम का आउटपुट है:

10 20 30 50 40 50 

वापसी के प्रकार के रूप में उस सीमा के नए सिरे पर पुनरावृत्ति होती है।

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

वेक्टर के नए सिरे से पुराने सिरे तक तत्वों को हटाने के लिए अब वेक्टर के इरेज़ फ़ंक्शन का उपयोग करें। इसके लिए O (1) समय की आवश्यकता होती है।

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

इसलिए यह विधि O (n) में काम करती है


0

*

C ++ समुदाय ने आपका अनुरोध सुना है :)

*

C ++ 20 अब इसे करने का एक आसान तरीका प्रदान करता है। यह उतना ही सरल है:

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

आप की जांच करनी चाहिए std :: मिटा और std :: erase_if

न केवल यह मूल्य के सभी तत्वों को हटा देगा (यहाँ '0'), यह इसे O (n) में करेगा समय की जटिलता में करेगा। जो बहुत ही बेहतरीन है जो आपको मिल सकता है।

यदि आपका संकलक C ++ 20 का समर्थन नहीं करता है, तो आपको मिटाने-हटाने के मुहावरे का उपयोग करना चाहिए :

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.