Belirli bir anahtarın bir C ++ std :: map'de olup olmadığı nasıl bulunur


451

Belirli bir anahtarın bir haritada olup olmadığını kontrol etmeye çalışıyorum ve biraz yapamıyorum:

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

p'de ne yazdırabilirim?


std::pair<iterator,bool> insert( const value_type& value );Geri döndüğü bool nedir? anahtarın mevcut olup olmadığını söyler mi?
krithikaGopalakrisnan

Yanıtlar:


692

kullanım map::find

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

105
Sadece belirli bir anahtarın var olup olmadığını kontrol etmek istiyorsanız, muhtemelen kullanmayı tercih map::count
edersiniz

10
@tomsmeding Bir std :: haritasında sadece tek bir anahtar vardır. Yani sayı ya 0 ya da 1 olacaktır. Biri diğerinden daha mı verimli?
goelakash

34
@goelakash neredeyse hiç; sadece countbir intsüre finddöndüren, bütün bir yineleyici döndürür. Eğer sonradan olacak eğer Açıkçası iterasyon :) inşaatını kaydetmek kullanmak varsa değerini, kullanım find ve sonucunu saklamak.
tomsmeding

9
@tomsmeding Bir multimap kullanıyorsanız, tüm kapsayıcının içine bakmanız gerekir. Bu durumda, find () daha hızlı olabilir.
Trevor Hickey

11
Hız arayanlar için: count ve findbenzersiz anahtarlar gerektiren haritalar kullanırken neredeyse aynı hızda. (1) Belirli bir siparişi sürdürmek için öğelere ihtiyacınız yoksa , neredeyse sürekli aramaları olan ve birkaç çiftten fazlasını saklarken çok faydalı olabilecek std :: unordered_map kullanın . (2) Varsa değeri kullanmak istiyorsanız, :: auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
aramanın sonucunu saklayın

305

Haritadaki belirli bir anahtarın mevcut olup olmadığını kontrol etmek için countüye işlevini aşağıdaki yollardan biriyle kullanın :

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

Dokümantasyon için map::finddiyor ki: "Başka üye işlev,map::count , sadece belirli bir anahtar olup olmadığını kontrol etmek için kullanılabilir."

Dokümantasyon için map::countdiyor ki: "Bir harita kapta tüm unsurları benzersiz olduğundan (eleman bulunursa), fonksiyon sadece 1 dönebilir ya da (aksi) sıfır."

Var olduğunu bildiğiniz bir anahtarla haritadan bir değer almak için map :: at :

value = m.at(key)

Aksine haritası :: operatörü [] , map::atbelirli şifre yoksa haritasında yeni bir anahtar oluşturmak değildir.


33
Her iki işlemi de yapacaksanız, bunun olup olmadığını kontrol edin ve ardından bir şey yapın. findBunun yerine kullanın . secondTarafından döndürülen yineleyici nitelik findkullanılabilir anahtarın değerini almak. countO zaman kullanırsanız atveya operator[]yalnızca bir tane kullanabileceğiniz iki işlem yapıyorsanız.
OdraEncoded

1
> 0, == 1 veya! = 0 yapmanıza gerek yoktur; C ++ bir if ifadesinde (koşul! = 0) yaptığı tam kontrol budur, bu yüzden kullanabilirsinizif(m.count(key))
jv110

6
@ jv110 Microsoft C ++ derleyicisi , ile arasında bir dökümle karşılaştığında bir uyarı verir . Benzer bir uyarı vermeyen diğer C ++ derleyicileri olmasına rağmen , amacı netleştirmek ve okunabilirliği artırmak için açık bir karşılaştırma kullanmayı tercih ederim . C # gibi diğer dillerin , ince programlama hataları getirme olasılığını önlemek için böyle bir örtük dönüşümü yasakladığını unutmayın . intbool
DavidRR

sayımın zaman karmaşıklığı nedir? Sadece O (1) operasyonu mu?
Mazeryt

1
@Mazeryt C ++ standart kütüphanesindeki bir sınıftan bahsettiğimiz göz önüne alındığında, kesinlikle öyle olacağını varsayıyorum. Sorunuzun dile bağlı olmayan bir tartışması için bkz. Karma tablolar gerçekten O (1) olabilir mi? .
DavidRR

47

C ++ 20 bize verdiği std::map::containsyapmak için.

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

35
Sanırım söyleyeceğim: Sonunda.
Erik Campobadal

2
Zaman hakkında .....
Ridhuvarshan

39

Şunları kullanabilirsiniz .find():

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

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

15
m.find == m.end() // not found 

Başka bir API kullanmak istiyorsanız 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";

12

Bence istiyorsun map::find. Eşitse m.find("f"), m.end()anahtar bulunamadı. Aksi takdirde find, bulunan öğeye işaret eden bir yineleyici döndürür.

Bunun nedeni p.first, akış eklemek için çalışmayan bir yineleyicidir. Son satırınızı olarak değiştirin cout << (p.first)->first;. pbir çift yineleyici, p.firstbir yineleyici, p.first->firstanahtar dizedir.

Bir haritanın belirli bir anahtar için yalnızca bir öğesi olabilir, bu yüzden equal_rangeçok kullanışlı değildir. Harita için tanımlanmıştır, çünkü tüm ilişkilendirilebilir kapsayıcılar için tanımlanmıştır, ancak çoklu harita için çok daha ilginçtir.


Aslında, bir haritanın yineleyicileri çifti olduğu için "önce cout << p.first->;
stefaanv

Cevabımı düzelttim, teşekkürler. Kodumu derlemediğim için aldım. Ve geçerliliğini kontrol etme konusunda (silinen bir yorumda) haklısın, ama sadece p.first'i neden yazdıramadığını açıklamaya çalışıyordum ve bunun geçersiz olduğu için değil - "f" nin bulunacağını biliyoruz. Hiç equal_range kullanmanızı önermediğim için, bunun için hata kontrol kodu göstermek üzereyim.
Steve Jessop

Vay canına, gerçekten SO'yu tarıyorsun. Sadece tamlık için ekliyordum, çünkü amacınız açıktı. Geçerliliğimi önceki yanıtıma ekledim, ancak yanıtınız beni dövdü, bu yüzden sildim, çünkü zaten bahsettiğiniz gibi çok fazla eklemedi.
stefaanv

Evet, sadece gördüm, çünkü yorumunuz benim yayınladığımda ortaya çıktı.
Steve Jessop

12

C++17ile biraz daha basitleştirdi If statement with initializer. Bu şekilde pastanızı alıp yiyebilirsiniz.

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

4
map<string, string> m;

kontrol anahtarı var ya da yok ve dönüş sayısı (haritada 0/1):

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

anahtarın var olup olmadığını kontrol edin ve tekrarlayıcıyı döndürün:

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

sorunuzu, hata kötü yol açtığı operator<<çünkü aşırı p.firstolduğunu map<string, string>, bunu çıktısını olamaz. bunu dene:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}

1
Bir yazım hatası var. "Cout" yerine "count" olarak değiştir
Rivka

1
Ve bu yazım hatası gerçekten birini bir başkasına atabilir, çünkü coutçok daha farklı bir şey anlamına gelebilircount
modulitos

4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Elbette meraklısı olmak istiyorsanız, her zaman bulunan bir işlevi ve bulunmayan bir işlevi alan bir işlevi şablonlayabilirsiniz, böyle bir şey:

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

Ve şu şekilde kullanın:

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

Bunun dezavantajı iyi bir isim ile geliyor, "find_and_execute" garip ve başımın üst kapalı daha iyi bir şey ile gelemiyorum ...


3

Bulgu sonucunu, 'm' haritasındaki gibi son ile karşılaştırırken dikkatli olun, çünkü tüm cevaplar haritanın üzerinde yapılmıştır: iterator i = m.find ("f");

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

eğer m.end () değerine eşitse, anahtar veya değeri yineleyici i ile yazdırmak gibi herhangi bir işlem yapmamalısınız ve bu bölümleme hatasına yol açacaktır.


0

Std :: map :: find ve std :: map :: count kodlarını karşılaştırarak, ilkinin bazı performans avantajı sağlayabileceğini söyleyebilirim:

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

0

Bu sorunun zaten bazı iyi cevapları olduğunu biliyorum ama bence çözümüm paylaşılmaya değer.

Her ikisi için de çalışır std::mapve std::vector<std::pair<T, U>>C ++ 11'den edinilebilir.

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

-5

Harita çiftini karşılaştırmak isterseniz bu yöntemi kullanabilirsiniz:

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

Bu yararlı bir tekniktir.


C ++ programlamaya yeni başlayan biri olarak, bu cevabın neden reddedildiğini merak ediyorum. Bu cevap neden popüler değil?
gromit190

3
@ gromit190 çünkü std :: map bu özelliğe zaten sahip olduğunda anahtarın var olup olmadığını görmek için başka bir veri yapısı kullanıyor. Bu ayrıca kimsenin uğraşmak istemediği bir bağımlılık olan iki veri yapısı arasında senkronizasyon gerektirir.
Lambage

-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }

3
Lütfen kodunuzu detaylandırın. Herhangi bir açıklaması olmayan bir pasaj, uzun vadede yardımcı olma eğiliminde değildir.
iBug
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.