Interchangeable key-value map

Started by
2 comments, last by Servant of the Lord 11 years, 3 months ago

WIth std::map<Key, Value>, I can get a value if I have a key, but not vice versa.

I need a map that can do both. Is there one that can do this?

An invisible text.
Advertisement
boost::bimap. Not currently aware if there are any C++11 alternatives...

You could probably do a std::vector<std::pair<Key1, Key2>>, and use std::find_if(), with the predicate checking only first or only second of the pair element, returning a match for either one. It wouldn't be sorted for rapid searches, though.

But now that I think about it, you could just do std::find_if() on a std::map when you need by-value lookups, if you don't need it frequently.

You could either use a lambda to do your search, if you use C++11, or you could use a functor like this:


template<typename KeyType, typename ValueType>
class SearchByValue
{
public:
	SearchByValue(ValueType value) : value(value)
	{	}
	
	bool operator()(const std::pair<KeyType, ValueType> &pair)
	{
		//Return true if the pair's value matches our value.
		return (pair.second == this->value);
	}
	
private:
	ValueType value;
};
 
int main(int argc, char *argv[])
{
	typedef std::map<std::string, int>::iterator MyMapIterator;
	
	std::map<std::string, int> myMap;
	
	myMap["One"] = 1;
	myMap["Two"] = 2;
	myMap["Three"] = 3;
	
	MyMapIterator it = std::find_if(myMap.begin(), myMap.end(), SearchByValue<std::string, int>(2));
	
	if(it == myMap.end())
	{
		std::cout << "Couldn't find it!" << std::endl;
	}
	else
	{
		std::cout << "The returned key-value pair is: (\"" << it->first << "\", " << it->second << ")" << std::endl;
	}
					 
	return 0;
}


Result: The returned key-value pair is: ("Two", 2)

The templated functor I gave should work for most maps.

Here's a function wrapping it up every nicer (still avoiding C++11 lambda and auto, just incase you can't use them):


/*
	A functor for doing a reverse value-to-key lookup on a map.
	
	Example:
	typedef std::map<std::string, int>::iterator MyMapIterator;
	
	std::map<std::string, int> myMap;
	
	myMap["One"] = 1;
	myMap["Two"] = 2;
	myMap["Three"] = 3;
	
	MyMapIterator it = std::find_if(myMap.begin(), myMap.end(), SearchByValue<std::string, int>(2));
*/
template<typename KeyType, typename ValueType>
class SearchByValue
{
public:
	SearchByValue(const ValueType &value) : value(value)
	{	}
	
	bool operator()(const std::pair<KeyType, ValueType> &pair)
	{
		//Return true if the pair's value matches our value.
		return (pair.second == this->value);
	}
	
private:
	ValueType value;
};

//Searches a map by value to find the key.
//Example usage:
//		std::string key = SearchMapByValue(map, 357 /* value */, "fallback");
template<typename MapType>
const typename MapType::key_type &SearchMapByValue(const MapType &map, const typename MapType::mapped_type &valueToFind,
										const typename MapType::key_type &fallback = typename MapType::key_type())
{
	typename MapType::const_iterator it = std::find_if(map.begin(), map.end(), SearchByValue<typename MapType::key_type, typename MapType::mapped_type>(valueToFind));
	
	if(it == map.end())
		return fallback;
	
	return it->first;
}


And you can use it as a simple function call, and all the templated types are automaticly detected by the map passed in:


std::map<std::string, int> myMap;
	
myMap["One"] = 1;
myMap["Two"] = 2;
myMap["Three"] = 3;
	
std::cout << "The key is: \"" << SearchMapByValue(myMap, 2) << "\"" << std::endl;


Result: The key is: "Two"

Put in a stand-alone header file, all you have to do is call SearchMapByValue(myMap, 2) or SearchMapByValue(myMap, 2, "fallback") and it'll "just work" with any variable type in a map that handles equality. (Searching for floats wouldn't be a good idea, but even some complex classes that have operator == would be fine)

This topic is closed to new replies.

Advertisement