remove_if std :: मैप के लिए बराबर है


118

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

प्रारंभ में मैंने उपयोग करने के बारे में सोचा था remove_ifलेकिन यह संभव नहीं है क्योंकि remove_if साहचर्य कंटेनर के लिए काम नहीं करता है।

क्या कोई "remove_if" समतुल्य एल्गोरिदम है जो मानचित्र के लिए काम करता है?

एक सरल विकल्प के रूप में, मैंने नक्शे के माध्यम से लूप करने और मिटाने के बारे में सोचा। लेकिन नक्शे के माध्यम से लूपिंग और एक सुरक्षित विकल्प को मिटा रहा है? (जैसे कि मिटाने के बाद पुनरावृत्तियां अमान्य हो जाती हैं)

मैंने निम्नलिखित उदाहरण का उपयोग किया:

bool predicate(const std::pair<int,std::string>& x)
{
    return x.first > 2;
}

int main(void) 
{

    std::map<int, std::string> aMap;

    aMap[2] = "two";
    aMap[3] = "three";
    aMap[4] = "four";
    aMap[5] = "five";
    aMap[6] = "six";

//      does not work, an error
//  std::remove_if(aMap.begin(), aMap.end(), predicate);

    std::map<int, std::string>::iterator iter = aMap.begin();
    std::map<int, std::string>::iterator endIter = aMap.end();

    for(; iter != endIter; ++iter)
    {
            if(Some Condition)
            {
                            // is it safe ?
                aMap.erase(iter++);
            }
    }

    return 0;
}

आपका क्या मतलब है कि remove_if काम नहीं करता है?
dirkgently

मैं मानचित्र में एक तत्व खोजने के लिए remove_if का उपयोग नहीं कर सकता, सही? इसने एक संकलन समय त्रुटि दी। क्या मैं कुछ भूल रहा हूँ?
ए.जे.

नहीं - यह एक अनुक्रम reordering द्वारा तत्वों को हटाने_ के रूप में काम नहीं करता है, चलती तत्व जो अंत में स्थिति को विफल करते हैं। इसलिए यह एक T [n] पर काम करता है, लेकिन नक्शा <T, U> नहीं।
MSalters

2
C + 11 के साथ, आप for(auto iter=aMap.begin(); iter!=aMap.end(); ){ ....}अव्यवस्था को कम करने के लिए उपयोग कर सकते हैं । बाकी जैसा कि दूसरों ने कहा है। इस प्रश्न ने मुझे अभी-अभी ;-)
अतुल कुमार

जवाबों:


111

लगभग।

for(; iter != endIter; ) {
     if (Some Condition) {
          iter = aMap.erase(iter);
     } else {
          ++iter;
     }
}

यदि आपने मूल रूप से दो बार इटरेटर को बढ़ाया होगा, यदि आपने उससे एक तत्व मिटाया था; आप संभावित रूप से उन तत्वों को छोड़ सकते हैं जिन्हें मिटाने की आवश्यकता है।

यह एक सामान्य एल्गोरिथ्म है जिसे मैंने कई स्थानों पर उपयोग और प्रलेखित देखा है।

[संपादित करें] आप सही हैं कि मिटाने के बाद पुनरावृत्तियों को अमान्य कर दिया जाता है, लेकिन केवल पुनरावृत्तियों को मिटाने वाले तत्व का संदर्भ दिया जाता है, अन्य पुनरावृत्तियों अभी भी मान्य हैं। इसलिए कॉल iter++में उपयोग करना erase()


4
मैं उलझन में हूं; जबकि आप (...?) के लिए क्यों इस्तेमाल करेंगे जबकि (...)? इसके अलावा, जबकि यह शायद काम करता है, .erase अगले एक का पुनरावृत्ति नहीं करता है? तो ऐसा लगता है जैसे (कुछ शर्त) ब्लॉग होना चाहिए iter = aMap.erase (iter) सबसे अधिक संगत होना चाहिए। शायद मुझे कुछ याद आ रहा है? मेरे पास आपके कुछ अनुभव की कमी है।
टैक्सियन

86
ध्यान दें, C ++ 11 में, सभी सहयोगी कंटेनर, सहित map, अगले पुनरावृत्त से लौटें erase(iter)। यह बहुत क्लीनर है iter = erase( iter )
पोटाटोज़वाटर

10
@taxilian (वर्ष देर से) जबकि () या के लिए () काम करेगा, लेकिन शब्दार्थ, लोग अक्सर ज्ञात सीमा पर पुनरावृत्ति के लिए () और अनजान संख्या में छोरों के लिए उपयोग करते हैं। चूंकि इस मामले में सीमा ज्ञात है (शुरुआत से, एंडआईटर तक ), के लिए () एक असामान्य विकल्प नहीं होगा, और संभवतः अधिक सामान्य होगा। लेकिन फिर, दोनों स्वीकार्य होंगे।
जैमिन ग्रे

4
@ टैक्स्टिलियन इससे भी महत्वपूर्ण: 'फॉर' के साथ, आप अपनी इटेरेटर परिभाषा को लूप के दायरे में रख सकते हैं, इसलिए यह आपके बाकी प्रोग्राम के साथ गड़बड़ नहीं करता है।
सेंचुरी

1
@athos यह प्रश्न निष्क्रिय स्वर में व्यक्त किया गया है, "यह अनुशंसित है।" कोई सार्वभौमिक सिफारिश नहीं है। मुझे लगता है कि मेरी अंतिम टिप्पणी सबसे सीधा तरीका है। इसमें इटरेटर वैरिएबल की दो प्रतियां शामिल हैं, जो यहां बताए गए अनुसार थोड़ी दक्षता खो देता है। यह आपकी कॉल है जो आपके लिए उपयुक्त है।
पोटाटोसवाटर

75

erase_if for std :: मानचित्र (और अन्य कंटेनर)

मैं इस बात के लिए निम्नलिखित टेम्पलेट का उपयोग करता हूं।

namespace stuff {
  template< typename ContainerT, typename PredicateT >
  void erase_if( ContainerT& items, const PredicateT& predicate ) {
    for( auto it = items.begin(); it != items.end(); ) {
      if( predicate(*it) ) it = items.erase(it);
      else ++it;
    }
  }
}

यह कुछ भी नहीं लौटाएगा, लेकिन यह std :: मैप से आइटम निकाल देगा।

उपयोग उदाहरण:

// 'container' could be a std::map
// 'item_type' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
  return /* insert appropriate test */;
});

दूसरा उदाहरण (आपको परीक्षण मान में पास होने की अनुमति देता है):

// 'test_value' is value that you might inject into your predicate.
// 'property' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4;  // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
  return item.property < test_value;  // or whatever appropriate test
});

3
@CodeAngry धन्यवाद - यह मुझे हमेशा अजीब लगता था कि यह पहले से मौजूद नहीं था std। मैं समझता हूं कि यह क्यों नहीं है std::map, लेकिन मुझे लगता है कि यह मानक पुस्तकालय में होना चाहिए।
लौह उद्धारकर्ता

3
C ++ 20 मेंstd::map और अन्य के लिए जोड़ा जाएगा ।
Roi Danton


3

मुझे यह दस्तावेज़ उत्कृष्ट SGI STL संदर्भ से मिला है :

मानचित्र में महत्वपूर्ण गुण है कि एक नए तत्व को मानचित्र में सम्मिलित करना पुनरावृत्तियों को अमान्य नहीं करता है जो मौजूदा तत्वों को इंगित करता है। मानचित्र से एक तत्व को मिटा देना भी किसी भी पुनरावृत्तियों को अमान्य नहीं करता है, सिवाय, ज़ाहिर है, पुनरावृत्तियों के लिए जो वास्तव में मिटाने वाले तत्व को इंगित करता है।

इसलिए, आपके पास जो इट्रेटर है, जो मिटाने वाले तत्व को इंगित कर रहा है, निश्चित रूप से अमान्य होगा। कुछ इस तरह से करें:

if (some condition)
{
  iterator here=iter++;
  aMap.erase(here)
}

3
यह मूल कोड से अलग नहीं है। iter ++ पुनरावर्तक को बढ़ाता है फिर वेतन वृद्धि से पहले तत्व पर इंगित करते हुए एक पुनरावृत्ति देता है।
स्टीव फोली

लेकिन पुनरावृत्ति को अमान्य नहीं किया जाएगा क्योंकि हम तब यहाँ की स्थिति पर मिट जाते हैं
1800 सूचना

@ 1800INFORMATION: एक फंक्शन कॉल दर्ज करना एक अनुक्रम बिंदु है, वेतन वृद्धि के प्रभाव का मूल्यांकन करने से पहले eraseकहा जाता है। तो वे वास्तव में बराबर हैं। फिर भी, मैं आपके संस्करण को मूल रूप से पसंद करूंगा।
पियरचेन

यह ऐरे या वेक्टर के लिए काम करता है, लेकिन स्टाल मैप में अप्रत्याशित परिणाम देगा।
शिकारी_टेक

2

मूल कोड में केवल एक समस्या है:

for(; iter != endIter; ++iter)
{
    if(Some Condition)
    {
        // is it safe ?
        aMap.erase(iter++);
    }
}

यहाँ iterलूप के लिए एक बार इंक्रीमेंट किया जाता है और दूसरी बार इरेज़ में, जो शायद कुछ अनंत लूप में खत्म हो जाएगा।



1

के निचले नोटों से:

http://www.sgi.com/tech/stl/PairAssociativeContainer.html

एक पेयर एसोसिएटिव कंटेनर उत्परिवर्तित पुनरावृत्तियाँ प्रदान नहीं कर सकता (जैसा कि तुच्छ Iterator आवश्यकताओं में परिभाषित किया गया है), क्योंकि एक उत्परिवर्तित पुनरावृत्ति का मान प्रकार निरुपित होना चाहिए, और युग्म निरुपित नहीं है। हालांकि, एक पेयर एसोसिएटिव कंटेनर पुनरावृत्तियों को प्रदान कर सकते हैं जो पूरी तरह से स्थिर नहीं हैं: पुनरावृत्तियों जैसे कि अभिव्यक्ति (* i)। सेकेंड = डी मान्य है।


1

प्रथम

मानचित्र में महत्वपूर्ण गुण है कि एक नए तत्व को मानचित्र में सम्मिलित करना पुनरावृत्तियों को अमान्य नहीं करता है जो मौजूदा तत्वों को इंगित करता है। मानचित्र से एक तत्व को मिटा देना भी किसी भी पुनरावृत्तियों को अमान्य नहीं करता है, सिवाय, ज़ाहिर है, पुनरावृत्तियों के लिए जो वास्तव में मिटाने वाले तत्व को इंगित करता है।

दूसरा, निम्नलिखित कोड अच्छा है

for(; iter != endIter; )
{
    if(Some Condition)
    {
        aMap.erase(iter++);
    }
    else
    {
        ++iter;
    }
}

किसी फ़ंक्शन को कॉल करते समय, उस फ़ंक्शन को कॉल करने से पहले मापदंडों का मूल्यांकन किया जाता है।

इसलिए जब iter ++ का मूल्यांकन मिटाने के लिए कॉल करने से पहले किया जाता है, तो itter का ++ ऑपरेटर वर्तमान आइटम को लौटा देगा और कॉल के बाद अगले आइटम को इंगित करेगा।


1

IMHO कोई remove_if()समकक्ष नहीं है।
आप एक नक्शे को पुनः व्यवस्थित नहीं कर सकते।
इसलिए remove_if()अपनी रुचि के जोड़े को अंत में नहीं रख सकते हैं जिस पर आप कॉल कर सकते हैं erase()


यह वाकई दुर्भाग्यपूर्ण है।
एलियोरकोड

1

आयरन उद्धारकर्ता के जवाब के आधार पर उन लोगों के लिए जो एक श्रेणी प्रदान करना चाहते हैं और साथ ही साथ इसे लेने वाले std कार्यात्मक की तर्ज पर।

template< typename ContainerT, class FwdIt, class Pr >
void erase_if(ContainerT& items, FwdIt it, FwdIt Last, Pr Pred) {
    for (; it != Last; ) {
        if (Pred(*it)) it = items.erase(it);
        else ++it;
    }
}

जिज्ञासु अगर ContainerTवस्तुओं को खोने और itter से प्राप्त करने का कोई तरीका है।


1
"अपर केस लेटर के बाद अंडरस्कोर के साथ शुरू होने वाले पहचानकर्ता कार्यान्वयन द्वारा सभी उपयोग के लिए आरक्षित हैं।"
YSC

0

स्टीव फोली का जवाब मुझे अधिक कुशल लगता है।

यहाँ एक और आसान लेकिन कम कुशल समाधान है :

समाधान का उपयोग remove_copy_ifहम उन मूल्यों को कॉपी करने के लिए करते हैं जो हम एक नए कंटेनर में चाहते हैं, फिर मूल कंटेनर की सामग्री को नए के साथ स्वैप करता है:

std::map<int, std::string> aMap;

...
//Temporary map to hold the unremoved elements
std::map<int, std::string> aTempMap;

//copy unremoved values from aMap to aTempMap
std::remove_copy_if(aMap.begin(), aMap.end(), 
                    inserter(aTempMap, aTempMap.end()),
                    predicate);

//Swap the contents of aMap and aTempMap
aMap.swap(aTempMap);

2
वह अक्षम लगता है।
एलियोरकोड

0

यदि आप 2 से अधिक कुंजी वाले सभी तत्वों को मिटाना चाहते हैं, तो सबसे अच्छा तरीका है

map.erase(map.upper_bound(2), map.end());

केवल सीमाओं के लिए काम करता है, किसी भी विधेय के लिए नहीं।


0

मैं इस तरह का उपयोग करता हूं

 std::map<int, std::string> users;    
 for(auto it = users.begin(); it <= users.end()) {
    if(<condition>){
      it = users.erase(it);
    } else {
    ++it;
    }
 }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.