322

What is the best way to determine if a STL map contains a value for a given key?

#include <map>

using namespace std;

struct Bar
{
    int i;
};

int main()
{
    map<int, Bar> m;
    Bar b = {0};
    Bar b1 = {1};

    m[0] = b;
    m[1] = b1;

    //Bar b2 = m[2];
    map<int, Bar>::iterator iter = m.find(2);
    Bar b3 = iter->second;

}

Examining this in a debugger, it looks like iter is just garbage data.

If I uncomment out this line:

Bar b2 = m[2]

The debugger shows that b2 is {i = 0}. (I'm guessing it means that using an undefined index will return a struct with all empty/uninitialized values?)

Neither of these methods is so great. What I'd really like is an interface like this:

bool getValue(int key, Bar& out)
{
    if (map contains value for key)
    {
        out = map[key];
        return true;
    }
    return false;
}

Does something along these lines exist?

jww
  • 97,681
  • 90
  • 411
  • 885
Nick Heiner
  • 119,074
  • 188
  • 476
  • 699
  • 2
    possible duplicate of [How to find if a given key exists in a C++ std::map](http://stackoverflow.com/questions/1939953/how-to-find-if-a-given-key-exists-in-a-c-stdmap) – OrangeDog Jul 24 '14 at 12:33

11 Answers11

432

As long as the map is not a multimap, one of the most elegant ways would be to use the count method

if (m.count(key))
    // key exists

The count would be 1 if the element is indeed present in the map.

vPraetor
  • 313
  • 1
  • 3
  • 13
pconnell
  • 4,551
  • 1
  • 14
  • 9
  • 36
    Won't this check *all* the keys even if it has found one already? That can get expensive fast... – mmdanziger Oct 24 '12 at 20:42
  • 45
    It will only count more than one key if used on a multimap. – Andrew Prock Mar 13 '13 at 23:38
  • 18
    @mmdanziger No, it won't be expensive: http://www.cplusplus.com/reference/map/map/count/ Count is logarithmic in size. – jgyou Nov 02 '14 at 17:46
  • 33
    The key exists, and then what? At that point you'd usually want to get the value for it, paying for another search (e.g. using `operator[]`). `find` gives you .NET's `TryGetValue` semantics, which is almost always what you (and specifically the OP) want. – Ohad Schneider Nov 06 '14 at 15:58
  • @OhadSchneider True, but it makes for a great assert(m.count(key)). – serine Oct 10 '15 at 00:55
  • @serine you can just as well do `assert(it != m.end())` – Ohad Schneider Oct 10 '15 at 11:07
  • @OhadSchneider, it you have the iterator. But if you just want to make an assert that the item is present and then use it you can assert(m.count(key)); and then m[key] – serine Oct 10 '15 at 13:32
  • @serine the point is that using `find` to get the iterator, asserting on it, and then using it (`it->second`) would be more efficient – Ohad Schneider Oct 10 '15 at 18:45
  • @OhadSchneider, I get that but assert() is not run in release code, so the efficiency is not impacted at all. When you know the key is there using the more readable map[key] is better I think, but you might still want to assert() just to make sure there are no mistakes and use also more readable map.count(key) insterad of map.find(key) == map.end() – serine Oct 11 '15 at 08:05
  • 2
    @serine Understood. Note that in case the key is missing in release the behavior will be different, as map[key] will return a newly default-constructed element value. – Ohad Schneider Oct 11 '15 at 21:49
  • The question is to check the presence of the value not key. – RaGa__M Feb 15 '17 at 10:56
333

Does something along these lines exist?

No. With the stl map class, you use ::find() to search the map, and compare the returned iterator to std::map::end()

so

map<int,Bar>::iterator it = m.find('2');
Bar b3;
if(it != m.end())
{
   //element found;
   b3 = it->second;
}

Obviously you can write your own getValue() routine if you want (also in C++, there is no reason to use out), but I would suspect that once you get the hang of using std::map::find() you won't want to waste your time.

Also your code is slightly wrong:

m.find('2'); will search the map for a keyvalue that is '2'. IIRC the C++ compiler will implicitly convert '2' to an int, which results in the numeric value for the ASCII code for '2' which is not what you want.

Since your keytype in this example is int you want to search like this: m.find(2);

Alan
  • 45,915
  • 17
  • 113
  • 134
  • 7
    How so? `find` indicates intent far better than `count` does. More over, `count` doesn't return the item. If you read the OP's question, he's wants to check for the existance, *and* return the element. `find` does that. `count` does not. – Alan May 23 '13 at 16:49
  • 1
    if (m.count(key)) b3 = m[key]; //or whatever – pconnell Jul 16 '13 at 19:25
  • 149
    I've always been curious as to what kind of weed were smoking the people who designed the whole stl API. – Trap Oct 08 '14 at 15:40
  • 2
    Alan I have to agree with @dynamic on this one, having to define an iterator and then compare it with end is not a natural way of saying that something does not exist. It seems far more straightforward to me to say that a certain element appears at least once in this map. Which is what count does. – PiersyP May 07 '17 at 20:07
  • @PiersyP If you only care about the existence of a key, you should use `std::set`. Usually if you're using a map, it's because you want to retrieve the element after determining it exists for a given key. In the example code, the author is retrieving the value after. The non-intuitiveness of the STL was/is a problem in c++, but that does not mean the above answer is incorrect. – Alan May 08 '17 at 04:23
  • I think it's possible to use `.count(key) > 0` – SomethingSomething May 22 '17 at 12:13
  • @Alan In my case I am creating an entry in a map that is expensive to create and so I want to only create it if it hasn't been created already. Your answer is correct its just that I would rather use pconnell's answer since it is much more concise. – PiersyP May 26 '17 at 17:24
  • 3
    @Claudiu C++20 adds just that. – pooya13 Jun 01 '19 at 01:10
  • 12
    Only a c++ programmer would answer with no, and then perfectly answer the question. – Daniel Lord Dec 21 '20 at 15:40
  • 2
    Pedantry is a first principal of c++ programming – Alan Dec 21 '20 at 18:17
163

I just noticed that with C++20, we will have

bool std::map::contains( const Key& key ) const;

That will return true if map holds an element with key key.

kebs
  • 6,387
  • 4
  • 41
  • 70
65

It already exists with find only not in that exact syntax.

if (m.find(2) == m.end() )
{
    // key 2 doesn't exist
}

If you want to access the value if it exists, you can do:

map<int, Bar>::iterator iter = m.find(2);
if (iter != m.end() )
{
    // key 2 exists, do something with iter->second (the value)
}

With C++0x and auto, the syntax is simpler:

auto iter = m.find(2);
if (iter != m.end() )
{
    // key 2 exists, do something with iter->second (the value)
}

I recommend you get used to it rather than trying to come up with a new mechanism to simplify it. You might be able to cut down a little bit of code, but consider the cost of doing that. Now you've introduced a new function that people familiar with C++ won't be able to recognize.

If you want to implement this anyway in spite of these warnings, then:

template <class Key, class Value, class Comparator, class Alloc>
bool getValue(const std::map<Key, Value, Comparator, Alloc>& my_map, int key, Value& out)
{
    typename std::map<Key, Value, Comparator, Alloc>::const_iterator it = my_map.find(key);
    if (it != my_map.end() )
    {
        out = it->second;
        return true;
    }
    return false;
}
stinky472
  • 6,737
  • 28
  • 27
9

amap.find returns amap::end when it does not find what you're looking for -- you're supposed to check for that.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
9

To succinctly summarize some of the other answers:

If you're not using C++ 20 yet, you can write your own mapContainsKey function:

bool mapContainsKey(std::map<int, int>& map, int key)
{
  if (map.find(key) == map.end()) return false;
  return true;
}

If you'd like to avoid many overloads for map vs unordered_map and different key and value types, you can make this a template function.

If you're using C++ 20 or later, there will be a built-in contains function:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}
cdahms
  • 3,402
  • 10
  • 49
  • 75
6

Check the return value of find against end.

map<int, Bar>::iterator it = m.find('2');
if ( m.end() != it ) { 
  // contains
  ...
}
Berk Soysal
  • 2,356
  • 1
  • 18
  • 17
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
1

You can create your getValue function with the following code:

bool getValue(const std::map<int, Bar>& input, int key, Bar& out)
{
   std::map<int, Bar>::iterator foundIter = input.find(key);
   if (foundIter != input.end())
   {
      out = foundIter->second;
      return true;
   }
   return false;
}
netjeff
  • 7,968
  • 4
  • 27
  • 30
Kip Streithorst
  • 425
  • 3
  • 8
1

Map provides 2 member functions to check if a given key exists in map with different return values i.e.

  1. std::map::find (returns iterator)

  2. std::map::count (returns count)

  • Check if map contains a key using std::map::count

It finds & returns the count of number of elements in map with key K. As map contains elements with unique key only. So, it will return 1 if key exists else 0.

  • Check if map contains a key using std::map::find

It checks if any element with given key ‘k’ exists in the map and if yes then it returns its iterator else it returns the end of map.

For more details and examples refer to below link(easy to understand explanation).

Credit: https://thispointer.com/how-check-if-a-given-key-exists-in-a-map-c/

Safin Ghoghabori
  • 516
  • 1
  • 6
  • 17
0

If you want to determine whether a key is there in map or not, you can use the find() or count() member function of map. The find function which is used here in example returns the iterator to element or map::end otherwise. In case of count the count returns 1 if found, else it returns zero(or otherwise).

if(phone.count(key))
{ //key found
}
else
{//key not found
}

for(int i=0;i<v.size();i++){
    phoneMap::iterator itr=phone.find(v[i]);//I have used a vector in this example to check through map you cal receive a value using at() e.g: map.at(key);
    if(itr!=phone.end())
        cout<<v[i]<<"="<<itr->second<<endl;
    else
        cout<<"Not found"<<endl;
}
Prashant Shubham
  • 456
  • 8
  • 20
0

Boost multindex can be used for proper solution. Following solution is not a very best option but might be useful in few cases where user is assigning default value like 0 or NULL at initialization and want to check if value has been modified.

Ex.
< int , string >
< string , int > 
< string , string > 

consider < string , string >
mymap["1st"]="first";
mymap["second"]="";
for (std::map<string,string>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
{
       if ( it->second =="" ) 
            continue;
}