32

I know find method finds the supplied key in std::map and return an iterator to the element. Is there anyway to find the value and get an iterator to the element? What I need to do is to check specified value exist in std::map. I have done this by looping all items in the map and comparing. But I wanted to know is there any better approach for this.

Here is what I have wrote

bool ContainsValue(Type_ value)
{
    bool found = false;
    Map_::iterator it = internalMap.begin(); // internalMap is std::map
    while(it != internalMap.end())
    {
        found = (it->second == value);
        if(found)
            break;
        ++it;
    }
    return found;
}

Edit

How about using another map internally which stores value,key combination. So I can call find on it? Is find() in std::map doing sequential search?

Thanks

Eric
  • 19,525
  • 19
  • 84
  • 147
Navaneeth K N
  • 15,295
  • 38
  • 126
  • 184

9 Answers9

20

You can use boost::multi_index to create a bidirectional map - you can use either value of the pair as a key to do a quick lookup.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
17

If you have access to the excellent boost library then you should be using boost::multi_index to create bidirectional map as Mark says. Unlike a std::map this allows you to look up by either the key or the value.

If you only have the STL to hand the following code will do the trick (templated to work with any kind of map where the mapped_type supports operator==):

#include <map>
#include <string>
#include <algorithm>
#include <iostream>
#include <cassert>

template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type, 
                                                      typename T::mapped_type, 
                                                      bool>
{
public:
    bool operator() (typename T::value_type &pair, 
                     typename T::mapped_type i) const
    {
        return pair.second == i;
    }
};


int main()
{
    typedef std::map<std::string, int> mapType;

    mapType map;

    map["a"] = 1;
    map["b"] = 2;
    map["c"] = 3;
    map["d"] = 4;
    map["e"] = 5;

    const int value = 3;

    std::map<std::string, int>::iterator it = std::find_if( map.begin(), map.end(), std::bind2nd(map_data_compare<mapType>(), value) );

    if ( it != map.end() )
    {
        assert( value == it->second);
        std::cout << "Found index:" << it->first << " for value:" << it->second << std::endl;
    }
    else
    {
        std::cout << "Did not find index for value:" << value << std::endl;
    }
}
CodeBuddy
  • 5,749
  • 1
  • 25
  • 30
15

How about using another map internally which stores value,key combination. So I can call find on it?

Yes: maintain two maps, with one map using one type of key and the other using the other.

Is find() in std::map doing sequential search?

No it's a binary search of a sorted tree: its speed is O(log(n)).

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • That makes sense. So will maintaining two maps give good performance than sequentially searching and finding value, right? – Navaneeth K N Feb 11 '09 at 03:57
  • 3
    It will take twice as long to insert or delete anything (using two maps instead one); but for a large number of elements, finding will be a lot faster because O(log(n)) is much smaller than the O(n) that's needed for a sequential search. – ChrisW Feb 11 '09 at 04:03
  • 6
    So simple yet so good (especially when using the boost library isn't an option). Have my upvote. I'd like to add that using `std::multimap` is probably better than `std::map` for the reversed map, because your original map can have duplicate values. Those duplicate values then become keys in the reversed map, so using `std::map` would lose data. – Dennis Sep 05 '11 at 06:15
6

Look into boost's bidirectional maps: http://www.boost.org/doc/libs/1_38_0/libs/bimap/doc/html/index.html

It lets both values act like a key.

Otherwise, iteration is the way to go.

Evan Teran
  • 87,561
  • 32
  • 179
  • 238
5

What you are requesting is precisely what std::find does (not the member function)

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
Vaidas
  • 1,494
  • 14
  • 21
Llopeth
  • 406
  • 5
  • 11
2

No, you have to loop over the std::map and check all values manually. Depending on what you want to do, you could wrap the std::map in a simple class that also caches all of the values that are inserted into the map in something that's easily search-able and doesn't allow duplicates, like a std::set. Don't inherit from the std::map (it doesn't have a virtual destructor!), but wrap it so that you can do something like this:

WrappedMap my_map< std::string, double >;
my_map[ "key" ] = 99.0;
std::set< double > values = my_map.values(); // should give back a set with only 99.0 in it

An alternative to rolling your own would be to use the Boost bidirectional map, which is easily found in the posts below or by Google.

It really depends on what you want to do, how often you want to do it, and how hard it is to roll your own little wrapper class versus installing and using Boost. I love Boost, so that's a good way to go - but there's something nice and complete about making your own wrapper class. You have the advantage of understanding directly the complexity of operations, and you may not need the full reverse mapping of values => keys that's provided by the Boost bidirectional map.

James Thompson
  • 46,512
  • 18
  • 65
  • 82
0

Not a very best option but might be useful in few cases where user is assigning default value like 0 or NULL at initialization.

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

I am adding this answer, if someone come here and looks for c++11 and above..

    //DECLARE A MAP
    std::map<int, int> testmap;

    //SAMPLE DATA
    testmap.insert(std::make_pair(1, 10));
    testmap.insert(std::make_pair(2, 20));
    testmap.insert(std::make_pair(3, 30));
    testmap.insert(std::make_pair(4, 20));

    //ELEMENTS WITH VALUE TO BE FOUND
    int value = 20;

    //RESULTS
    std::map<int, int> valuesMatching;

    //ONE STEP TO FIND ALL MATCHING MAP ELEMENTS
    std::copy_if(testmap.begin(), testmap.end(), std::inserter(valuesMatching, valuesMatching.end()), [value](const auto& v) {return v.second == value; });
Pavan Chandaka
  • 11,671
  • 5
  • 26
  • 34
-4

Possible that I don't fully understand what you're trying to accomplish. But to simply test whether or not a map contains a value, I believe you can use the std::map's built in find.

bool ContainsValue(Type_ value)
{
    return (internalMap.find(value) != internalMap.end());
}
Ternary
  • 2,401
  • 3
  • 31
  • 54
  • 7
    `std::map::find` searches by key, he is trying to search by value. – Dennis Sep 05 '11 at 06:12
  • You're absolutely right. I guess short of iterating and searching the map by hand, you should use a bidirectional map like the already suggested Boost.MultiIndex. – Ternary Sep 16 '11 at 15:38