ChatGPT解决这个技术问题 Extra ChatGPT

Determine if map contains a value for a key?

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?


v
vPraetor

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.


Won't this check all the keys even if it has found one already? That can get expensive fast...
It will only count more than one key if used on a multimap.
@mmdanziger No, it won't be expensive: cplusplus.com/reference/map/map/count Count is logarithmic in size.
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.
@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.
A
Alan

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


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.
I've always been curious as to what kind of weed were smoking the people who designed the whole stl API.
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.
@Claudiu C++20 adds just that.
Only a c++ programmer would answer with no, and then perfectly answer the question.
k
kebs

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.


Finally an answer that talks about this function! (C++20)
Finally ? Thanks, but it's almost 2 years old ! ;-)
Hopefully people will scroll to this solution and not use the deprecated ones anymore. :)
it only took them till 2020, maybe they will have modules by 2050.
I thought for sure I was missing something when cppreference said "(C++20)" next to this function...like seriously, it's such an obvious function to have; I'm shocked it took them this long.
s
stinky472

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

A
Alex Martelli

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


c
cdahms

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
}

B
Berk Soysal

Check the return value of find against end.

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

n
netjeff

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

I believe line 6 should be out = foundIter->second
I fixed Kip's answer to correctly show out = foundIter->second rather than out = *foundIter
S
Safin Ghoghabori

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

std::map::find (returns iterator) 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/


P
Prashant Shubham

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

S
Shail Gautam

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