So finden Sie heraus, ob ein bestimmter Schlüssel in einer C ++ std :: map vorhanden ist

450

Ich versuche zu überprüfen, ob sich ein bestimmter Schlüssel in einer Karte befindet und kann es etwas nicht:

typedef map<string,string>::iterator mi;
map<string, string> m;
m.insert(make_pair("f","++--"));
pair<mi,mi> p = m.equal_range("f");//I'm not sure if equal_range does what I want
cout << p.first;//I'm getting error here

Wie kann ich also drucken, was in p steht?

Wir können nichts tun
quelle
std::pair<iterator,bool> insert( const value_type& value );Was ist der Bool, den es zurückgibt? sagt es, ob der Schlüssel bereits vorhanden ist oder nicht?
krithikaGopalakrisnan

Antworten:

691

Verwenden map::find

if ( m.find("f") == m.end() ) {
  // not found
} else {
  // found
}

quelle
105
Wenn Sie nur überprüfen möchten, ob ein bestimmter Schlüssel vorhanden ist, verwenden Sie wahrscheinlich liebermap::count
tomsmeding
10
@tomsmeding In einer std :: map gibt es nur einen einzigen Schlüssel. Die Anzahl ist also entweder 0 oder 1. Ist einer effizienter als der andere?
Goelakash
34
@goelakash kaum; Es ist nur so, dass counteine intWeile findeinen ganzen Iterator zurückgibt. Sie speichern die Konstruktion des Iterators :) Wenn Sie den Wert anschließend verwenden möchten, falls vorhanden, verwenden Sie natürlich find und speichern Sie das Ergebnis.
Tomsmeding
9
@tomsmeding Wenn Sie eine Multimap verwenden, müssen Sie den gesamten Container durchsuchen. In diesem Fall ist find () möglicherweise schneller.
Trevor Hickey
11
Für diejenigen, die Geschwindigkeit suchen: count und findin der Geschwindigkeit nahezu identisch sind, wenn Karten verwendet werden, für die eindeutige Schlüssel erforderlich sind. (1) Wenn Sie die Elemente nicht benötigen, um eine bestimmte Reihenfolge aufrechtzuerhalten, verwenden Sie std :: unordered_map , das nahezu konstante Suchvorgänge aufweist und beim Speichern von mehr als einigen Paaren sehr nützlich sein kann. (2) Wenn Sie den Wert verwenden möchten, falls vorhanden, speichern Sie das Ergebnis von :: find und verwenden Sie den Iterator, um zwei auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
Suchvorgänge
305

Verwenden Sie die countElementfunktion auf eine der folgenden Arten , um zu überprüfen, ob ein bestimmter Schlüssel in der Karte vorhanden ist :

m.count(key) > 0
m.count(key) == 1
m.count(key) != 0

In der Dokumentation zu map::findheißt es: "Eine andere Mitgliedsfunktion map::countkann verwendet werden, um nur zu überprüfen, ob ein bestimmter Schlüssel vorhanden ist."

In der Dokumentation zu map::countheißt es: "Da alle Elemente in einem Kartencontainer eindeutig sind, kann die Funktion nur 1 (wenn das Element gefunden wird) oder Null (andernfalls) zurückgeben."

Um einen Wert aus der Karte über einen Schlüssel abrufen , dass Sie wissen , existieren, die Verwendung der Karte :: at :

value = m.at(key)

Im Gegensatz Karte :: operator [] , map::atwird nicht einen neuen Schlüssel in der Karte erstellen , wenn der angegebene Schlüssel existiert nicht.

DavidRR
quelle
33
Wenn Sie beide Vorgänge ausführen möchten, überprüfen Sie, ob sie vorhanden sind, und tun Sie dann etwas dagegen. Verwenden Sie findstattdessen. Das secondAttribut des von zurückgegebenen Iterators findkann verwendet werden, um den Wert des Schlüssels abzurufen. Wenn Sie countdann verwenden atoder operator[]zwei Operationen ausführen, bei denen Sie nur eine hätten verwenden können.
OdraEncoded
1
Sie müssen nicht> 0, == 1 oder! = 0 tun; Das ist die genaue Prüfung, die C ++ in einer if-Anweisung durchführt (Bedingung! = 0), also können Sie einfachif(m.count(key))
jv110
6
@ jv110 Der Microsoft C ++ - Compiler gibt eine Warnung aus, wenn er auf eine Umwandlung von intbis stößt bool. Obwohl es andere C ++ - Compiler gibt, die keine ähnliche Warnung ausgeben, bevorzuge ich die Verwendung eines expliziten Vergleichs , um die Absicht klar zu machen und die Lesbarkeit zu verbessern. Beachten Sie, dass andere Sprachen wie C # eine solche implizite Konvertierung verbieten , um die Möglichkeit subtiler Programmierfehler zu vermeiden.
DavidRR
Was ist die zeitliche Komplexität der Zählung? Ist es nur eine O (1) -Operation?
Mazeryt
1
@ Mazeryt Angesichts der Tatsache, dass es sich um eine Klasse in der C ++ - Standardbibliothek handelt, würde ich dies mit Sicherheit annehmen. Eine sprachunabhängige Diskussion Ihrer Frage finden Sie unter Können Hash-Tabellen wirklich O (1) sein? .
DavidRR
47

C ++ 20 gibt uns std::map::containsdie Möglichkeit dazu.

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> example = {{1, "One"}, {2, "Two"}, 
                                     {3, "Three"}, {42, "Don\'t Panic!!!"}};

    if(example.contains(42)) {
        std::cout << "Found\n";
    } else {
        std::cout << "Not found\n";
    }
}
Denis Sablukov
quelle
34
Ich denke, ich werde es sagen: Endlich.
Erik Campobadal
2
Über die Zeit .....
Ridhuvarshan
39

Sie können verwenden .find():

map<string,string>::iterator i = m.find("f");

if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }
Thomas Bonini
quelle
15
m.find == m.end() // not found 

Wenn Sie eine andere API verwenden möchten, suchen Sie go for m.count(c)>0

 if (m.count("f")>0)
      cout << " is an element of m.\n";
    else 
      cout << " is not an element of m.\n";
aJ.
quelle
12

Ich denke du willst map::find. Wenn m.find("f")gleich ist, m.end()wurde der Schlüssel nicht gefunden. Andernfalls gibt find einen Iterator zurück, der auf das gefundene Element zeigt.

Der Fehler liegt daran, dass p.firstes sich um einen Iterator handelt, der beim Einfügen von Streams nicht funktioniert. Ändern Sie Ihre letzte Zeile in cout << (p.first)->first;. pist ein Paar von Iteratoren, p.firstist ein Iterator, p.first->firstist die Schlüsselzeichenfolge.

Eine Karte kann immer nur ein Element für einen bestimmten Schlüssel haben, equal_rangeist also nicht sehr nützlich. Es ist für die Karte definiert, da es für alle assoziativen Container definiert ist, für Multimap jedoch viel interessanter.

Steve Jessop
quelle
Da es sich um ein Paar von Iteratoren für eine Karte handelt, sollte es "cout << p.first-> first" sein.
stefaanv
Ich habe meine Antwort korrigiert, danke. Das bekomme ich, wenn ich meinen Code nicht kompiliere. Und Sie haben Recht (in einem gelöschten Kommentar), die Gültigkeit zu überprüfen, aber ich habe nur versucht zu erklären, warum er p.first nicht drucken konnte, und nicht, weil es ungültig ist - wir wissen, dass "f" gefunden wird. Da ich die Verwendung von equal_range überhaupt nicht empfehle, werde ich dafür keinen Fehlerprüfcode anzeigen.
Steve Jessop
Wow, du scannst wirklich SO. Ich habe es der Vollständigkeit halber nur hinzugefügt, weil Ihr Standpunkt klar war. Ich habe die Gültigkeitsprüfung zu meiner vorherigen Antwort hinzugefügt, aber Ihre Antwort hat mich geschlagen, also habe ich sie gelöscht, weil sie sowieso nicht so viel hinzugefügt hat, wie Sie erwähnt haben.
stefaanv
Ja, ich habe es nur gesehen, weil Ihr Kommentar erschien, als ich meinen gepostet habe.
Steve Jessop
12

C++17vereinfachte dies etwas mehr mit einem If statement with initializer. Auf diese Weise können Sie Ihren Kuchen haben und ihn auch essen.

if ( auto it{ m.find( "key" ) }; it != std::end( m ) ) 
{
    // Use `structured binding` to get the key
    // and value.
    auto[ key, value ] { *it };

    // Grab either the key or value stored in the pair.
    // The key is stored in the 'first' variable and
    // the 'value' is stored in the second.
    auto mkey{ it->first };
    auto mvalue{ it->second };

    // That or just grab the entire pair pointed
    // to by the iterator.
    auto pair{ *it };
} 
else 
{
   // Key was not found..
}
WBuck
quelle
4
map<string, string> m;

Prüfschlüssel vorhanden oder nicht, und Rückgabewert von tritt auf (0/1 in der Karte):

int num = m.count("f");  
if (num>0) {    
    //found   
} else {  
    // not found  
}

Überprüfen Sie, ob der Schlüssel vorhanden ist oder nicht, und geben Sie den Iterator zurück:

map<string,string>::iterator mi = m.find("f");  
if(mi != m.end()) {  
    //found  
    //do something to mi.  
} else {  
    // not found  
}  

in Ihrer Frage, verursacht der Fehler durch schlechte operator<<Überlastung, da p.firstist map<string, string>, können Sie es nicht drucken aus. Versuche dies:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}
hustljian
quelle
1
Du hast einen Tippfehler. Ändern Sie "cout" in "count"
Rivka
1
Und dieser Tippfehler kann wirklich jemanden coutcount
abschrecken
4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Wenn Sie schicker werden möchten, können Sie natürlich jederzeit eine Funktion vorlegen, die auch eine gefundene Funktion und eine nicht gefundene Funktion enthält.

template <typename T, typename Key, typename FoundFunction, typename NotFoundFunction>
void find_and_execute(const T& container, const Key& key, FoundFunction found_function, NotFoundFunction not_found_function)
{
    auto& it = container.find(key);
    if (it != std::end(container))
    {
        found_function(key, it->second);
    }
    else
    {
        not_found_function(key);
    }
}

Und benutze es so:

    std::map<int, int> some_map;
    find_and_execute(some_map, 1,
        [](int key, int value){ std::cout << "key " << key << " found, value: " << value << std::endl; },
        [](int key){ std::cout << "key " << key << " not found" << std::endl; });

Der Nachteil dabei ist, dass ein guter Name "find_and_execute" unangenehm ist und ich mir nichts Besseres einfallen lassen kann ...

Lambage
quelle
3

Seien Sie vorsichtig beim Vergleichen des Suchergebnisses mit dem Ende wie für map 'm', da alle Antworten über map :: iterator i = m.find ("f") erfolgen.

 if (i == m.end())
 {
 }
 else
 {
 }  

Sie sollten nicht versuchen, eine Operation wie das Drucken des Schlüssels oder Werts mit dem Iterator i auszuführen, wenn dieser gleich m.end () ist. Andernfalls führt dies zu einem Segmentierungsfehler.

Invictus
quelle
0

Wenn man den Code von std :: map :: find und std :: map :: count vergleicht, würde ich sagen, dass der erste einen Leistungsvorteil bringen kann:

const_iterator find(const key_type& _Keyval) const
    {   // find an element in nonmutable sequence that matches _Keyval
    const_iterator _Where = lower_bound(_Keyval); // Here one looks only for lower bound
    return (_Where == end()
        || _DEBUG_LT_PRED(this->_Getcomp(),
            _Keyval, this->_Key(_Where._Mynode()))
                ? end() : _Where);
    }

size_type count(const key_type& _Keyval) const
    {   // count all elements that match _Keyval
    _Paircc _Ans = equal_range(_Keyval); // Here both lower and upper bounds are to be found, which is presumably slower.
    size_type _Num = 0;
    _Distance(_Ans.first, _Ans.second, _Num);
    return (_Num);
    }
Hoffnung
quelle
0

Ich weiß, dass diese Frage bereits einige gute Antworten hat, aber ich denke, meine Lösung ist es wert, geteilt zu werden.

Es funktioniert für beide std::mapund std::vector<std::pair<T, U>>ist in C ++ 11 verfügbar.

template <typename ForwardIterator, typename Key>
bool contains_key(ForwardIterator first, ForwardIterator last, Key const key) {
    using ValueType = typename std::iterator_traits<ForwardIterator>::value_type;

    auto search_result = std::find_if(
        first, last,
        [&key](ValueType const& item) {
            return item.first == key;
        }
    );

    if (search_result == last) {
        return false;
    } else {
        return true;
    }
}
Nussknacker
quelle
-5

Wenn Sie ein Kartenpaar vergleichen möchten, können Sie folgende Methode verwenden:

typedef map<double, double> TestMap;
TestMap testMap;
pair<map<double,double>::iterator,bool> controlMapValues;

controlMapValues= testMap.insert(std::pair<double,double>(x,y));
if (controlMapValues.second == false )
{
    TestMap::iterator it;
    it = testMap.find(x);

    if (it->second == y)
    {
        cout<<"Given value is already exist in Map"<<endl;
    }
}

Dies ist eine nützliche Technik.

EmreS
quelle
Als Anfänger in der C ++ - Programmierung bin ich sehr gespannt, warum diese Antwort abgelehnt wird. Warum ist diese Antwort unbeliebt?
Gromit190
3
@ gromit190, da eine ganz andere Datenstruktur verwendet wird, um festzustellen, ob der Schlüssel vorhanden ist, wenn std :: map diese Funktion bereits besitzt. Dies würde auch eine Synchronisation zwischen den beiden Datenstrukturen erfordern, was eine Abhängigkeit ist, mit der sich niemand befassen möchte.
Lambage
-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }
Muhammad Ahmad Zafar
quelle
3
Bitte erläutern Sie Ihren Code. Ein Snippet ohne Erklärung ist auf lange Sicht nicht hilfreich.
iBug