Does the map::find
method support case insensitive search? I have a map as follows:
map > directory;
<
The Compare element of the map template defaults to a binary comparison class "less". Look at the implementation:
http://www.cplusplus.com/reference/std/functional/less/
You can likely create your own class that derives from binary_function (the parent class to less) and do the same comparison without case sensitivity.
It does not by default. You will have to provide a custom comparator as a third argument. Following snippet will help you...
/************************************************************************/
/* Comparator for case-insensitive comparison in STL assos. containers */
/************************************************************************/
struct ci_less : std::binary_function<std::string, std::string, bool>
{
// case-independent (ci) compare_less binary function
struct nocase_compare : public std::binary_function<unsigned char,unsigned char,bool>
{
bool operator() (const unsigned char& c1, const unsigned char& c2) const {
return tolower (c1) < tolower (c2);
}
};
bool operator() (const std::string & s1, const std::string & s2) const {
return std::lexicographical_compare
(s1.begin (), s1.end (), // source range
s2.begin (), s2.end (), // dest range
nocase_compare ()); // comparison
}
};
Use it like std::map< std::string, std::vector<std::string>, ci_less > myMap;
NOTE: std::lexicographical_compare has some nitty-gritty details. String comparison isn't always straightforward if you consider locales. See this thread on c.l.c++ if interested.
UPDATE: With C++11 std::binary_function
is deprecated and is unnecessary as the types are deduced automatically.
struct ci_less
{
// case-independent (ci) compare_less binary function
struct nocase_compare
{
bool operator() (const unsigned char& c1, const unsigned char& c2) const {
return tolower (c1) < tolower (c2);
}
};
bool operator() (const std::string & s1, const std::string & s2) const {
return std::lexicographical_compare
(s1.begin (), s1.end (), // source range
s2.begin (), s2.end (), // dest range
nocase_compare ()); // comparison
}
};
You can instantiate std::map
with three parameters: type of keys, type of values, and comparison function -- a strict weak ordering (essentially, a function or functor behaving like operator<
in terms of transitivity and anti-reflexivity) of your liking. Just define the third parameter to do "case-insensitive less-than" (e.g. by a <
on the lowercased strings it's comparing) and you'll have the "case-insensitive map" you desire!
In case you don't want to touch the map type (to keep it's original simplicity and efficiency), but don't mind using a slower case-insensitive find function (O(N)):
string to_lower(string s) {
transform(s.begin(), s.end(), s.begin(), (int(*)(int)) tolower );
return s;
}
typedef map<string, int> map_type;
struct key_lcase_equal {
string lcs;
key_lcase_equal(const string& s) : lcs(to_lower(s)) {}
bool operator()(const map_type::value_type& p) const {
return to_lower(p.first) == lcs;
}
};
map_type::iterator find_ignore_case(map_type& m, const string& s) {
return find_if(m.begin(), m.end(), key_lcase_equal(s));
}
PS: Maybe it was Roger Pate's idea, but not sure, since some details were a bit off (std::search?, direct string comparator?)
For C++11 and beyond:
#include <strings.h>
#include <map>
#include <string>
namespace detail
{
struct CaseInsensitiveComparator
{
bool operator()(const std::string& a, const std::string& b) const noexcept
{
return ::strcasecmp(a.c_str(), b.c_str()) < 0;
}
};
} // namespace detail
template <typename T>
using CaseInsensitiveMap = std::map<std::string, T, detail::CaseInsensitiveComparator>;
int main(int argc, char* argv[])
{
CaseInsensitiveMap<int> m;
m["one"] = 1;
std::cout << m.at("ONE") << "\n";
return 0;
}