it-swarm.com.de

remove_if entspricht für std :: map

Ich habe versucht, eine Reihe von Elementen aus der Karte basierend auf bestimmten Bedingungen zu löschen. Wie mache ich das mit STL-Algorithmen?

Anfangs dachte ich an remove_if, aber es ist nicht möglich, da remove_if nicht für assoziative Container funktioniert.

Gibt es einen "remove_if" äquivalenten Algorithmus, der für die Map funktioniert?

Als einfache Option dachte ich daran, die Karte zu durchlaufen und zu löschen. Aber ist das Durchlaufen der Karte und das Löschen eine sichere Option (da Iteratoren nach dem Löschen ungültig werden)

Ich habe folgendes Beispiel verwendet:

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;
}
102
aJ.

Fast. 

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

Das, was Sie ursprünglich hatten, würde den Iterator erhöhen zweimal , wenn Sie ein Element daraus löschten; Sie könnten möglicherweise Elemente überspringen, die gelöscht werden müssen.

Dies ist ein allgemeiner Algorithmus, den ich an vielen Stellen verwendet und dokumentiert habe.

[BEARBEITEN] Es stimmt, dass Iteratoren nach einem Löschen ungültig werden, aber nur Iteratoren, die auf das gelöschte Element verweisen, sind gültig. Verwenden Sie daher iter ++ im Aufruf von erase ().

105
Steve Folly

erase_if für std :: map (und andere Container)

Ich verwende die folgende Vorlage für genau dieses Ding.

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

Dies gibt nichts zurück, entfernt jedoch die Elemente aus der std :: map.

Anwendungsbeispiel:

// '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 */;
});

Zweites Beispiel (ermöglicht das Übergeben eines Testwerts):

// '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
});
64
Iron Savior

Ich habe diese Dokumentation aus der exzellenten SGI STL-Referenz erhalten :

Map hat die wichtige Eigenschaft, dass Einfügen eines neuen Elements in eine Map macht Iteratoren nicht ungültig, dass auf vorhandene Elemente verweisen. Löschen eines Das Element aus einer Map hat auch keine alle Iteratoren außer ungültig machen. natürlich für Iteratoren, die tatsächlich zeigen Sie auf das Element, das .__ ist. gelöscht.

Der Iterator, der auf das zu löschende Element zeigt, wird natürlich ungültig. Tun Sie so etwas:

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

Der Originalcode hat nur ein Problem:

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

Hier wird iter einmal in der for-Schleife und ein anderes Mal in erase inkrementiert, was wahrscheinlich in einer Endlosschleife endet.

2
partha biswas

Zuerst

Map hat die wichtige Eigenschaft, dass das Einfügen eines neuen Elements in eine Map Iteratoren nicht ungültig macht, die auf vorhandene Elemente verweisen. Das Löschen eines Elements aus einer Map macht auch keine Iteratoren ungültig, außer natürlich für Iteratoren, die tatsächlich auf das gelöschte Element zeigen.

Zweitens ist der folgende Code gut

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

Beim Aufruf einer Funktion werden die Parameter vor dem Aufruf dieser Funktion ausgewertet.

Wenn also iter ++ vor dem Aufruf zum Löschen ausgewertet wird, gibt der ++ - Operator des Iterators das aktuelle Element zurück und zeigt nach dem Aufruf auf das nächste Element.

1
Vincent

Basierend auf Iron Saviors Antwort Für diejenigen, die einen Bereich eher in Richtung std-funktionaler Iteratoren anbieten möchten.

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

Neugierig, ob es eine Möglichkeit gibt, die ContainerT-Elemente zu verlieren und vom Iterator zu erhalten.

1
Greg Domjan

IMHO gibt es kein remove_if()-Äquivalent.
Sie können eine Karte nicht neu anordnen.
remove_if() kann also Ihre Interessenpaare nicht an das Ende setzen, an dem Sie erase() aufrufen können.

1
user109134

Aus den unteren Anmerkungen von:

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

ein Pair Associative Container kann keine veränderlichen Iteratoren (wie in den Trivial Iterator-Anforderungen definiert) bereitstellen, da der Werttyp eines veränderbaren Iterators zuweisbar sein muss und das Paar nicht zuweisbar ist. Ein Pair Associative Container kann jedoch Iteratoren bereitstellen, die nicht vollständig konstant sind: Iteratoren, sodass der Ausdruck (* i) .second = d gültig ist. 

1
piotr

Nun ist std::experimental::erase_if im Header <experimental/map> verfügbar.

Siehe: http://en.cppreference.com/w/cpp/experimental/map/erase_if

1
user1633272

Steve Follys Antwort Ich fühle mich effizienter.

Hier ist eine weitere einfache, aber weniger effiziente Lösung

Die Lösung verwendet remove_copy_if, um die gewünschten Werte in einen neuen Container zu kopieren, und tauscht dann den Inhalt des Originalcontainers mit denen des neuen Containers:

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);
0
aJ.

Wenn Sie alle Elemente mit einem Schlüssel größer als 2 löschen möchten, ist der beste Weg

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

Funktioniert nur für Bereiche, jedoch nicht für Prädikate.

0
Tadeusz Kopec

Ich benutze so

 std::map<int, std::string> users;    
 for(auto it = users.begin(); it <= users.end()) {
    if(<condition>){
      it = users.erase(it);
    } else {
    ++it;
    }
 }
0
voltento