How can I make the map::find operation case insensitive?

Viewed 40933

Does the map::find method support case insensitive search? I have a map as follows:

map<string, vector<string> > directory;

and want the below search to ignore case:

directory.find(search_string);
12 Answers

I use the following:

bool str_iless(std::string const & a, 
               std::string const & b)
{
    return boost::algorithm::lexicographical_compare(a, b,  
                                                     boost::is_iless());
}
std::map<std::string, std::string, 
         boost::function<bool(std::string const &, 
                              std::string const &)> 
         > case_insensitive_map(&str_iless);

I'd like to present a short solution without using Boost or templates. Since C++11 you can also provide a lambda expression as custom comparator to your map. For a POSIX-compatible system, the solution could look as follows:

auto comp = [](const std::string& s1, const std::string& s2) {
    return strcasecmp(s1.c_str(), s2.c_str()) < 0;
};
std::map<std::string, std::vector<std::string>, decltype(comp)> directory(comp);

Code on Ideone

For Window, strcasecmp() does not exist, but you can use _stricmp() instead:

auto comp = [](const std::string& s1, const std::string& s2) {
    return _stricmp(s1.c_str(), s2.c_str()) < 0;
};
std::map<std::string, std::vector<std::string>, decltype(comp)> directory(comp);

Note: Depending on your system and whether you have to support Unicode or not, you might need to compare strings in a different way. This Q&A gives a good start.

This is the cross-platform standard c++ solution unlike strcasecmp (which is only available for posix), without using any external libraries like boost, that I have personally written. It takes advantage of the comparison function of std::map.

#include <algorithm>                                                            
#include <cctype>                                                               
#include <iostream>                                                             
#include <map>                                                                  
#include <string>                                                               
                                                                                
bool caseInsensitiveCompare(const std::string& a, const std::string& b) {       
  std::string aLower = a;                                                       
  std::string bLower = b;                                                       
  std::transform(aLower.begin(), aLower.end(), aLower.begin(), [](unsigned char c){ return std::tolower(c); });
  std::transform(bLower.begin(), bLower.end(), bLower.begin(), [](unsigned char c){ return std::tolower(c); });
  return aLower < bLower;                                                       
};                                                                              
                                                                                
int main()                                                                      
{                                                                               
  std::map<std::string, std::string, decltype(&caseInsensitiveCompare)> myMap(&caseInsensitiveCompare);
  myMap.insert({"foo", "foo"});                                                 
  myMap.insert({"bar", "bar"});                                                 
  myMap.insert({"baz", "baz"});                                                 
                                                                                
  auto it = myMap.find("FoO");                                                  
  if (it != myMap.end()) std::cout << "Found FoO: " << it->second << std::endl;      
  else std::cout << "Not found FoO" << std::endl;                              
                                                                                
  it = myMap.find("foo");                                                       
  if (it != myMap.end()) std::cout << "Found foo: " << it->second << std::endl;    
  else std::cout << "Not found foo" << std::endl;                            
                                                                                
  it = myMap.find("not contained");                                                       
  if (it != myMap.end()) std::cout << "Found not contained: " << it->second << std::endl;    
  else std::cout << "Not found notcontained" << std::endl;                            
                                                                                                                                                                          
  return 0;                                                                     
}
Related