how to make stl::map key case insensitive [duplicate] - c++

This question already has answers here:
How can I make the map::find operation case insensitive?
(12 answers)
Closed 9 years ago.
I am new to stl's. Here is my below program.
typedef pair<string, int> p;
int main(int argc, char *argv[])
{
map<string,int> st;
st.insert(p("hello",1)); //Inserted "hello" as key to map.
st.insert(p("HELLO",1)); //Inserted "HELLO" as key to map.
cout<<"size="<<st.size()<<endl; //Output is 2 because two records found "hello" and "HELLO"
return 0;
}
I don't want to take account of the repeated case changes(upper case to lower case words or vice-versa). Here "st.insert(p("HELLO",1));" should fail, hence the no. of records should be "1" instead of "2". Is there any flag setup or like so?
I was unable to find the related questions hence posted this question.
Any help is thankful.

Use a custom comparator:
struct comp {
bool operator() (const std::string& lhs, const std::string& rhs) const {
return stricmp(lhs.c_str(), rhs.c_str()) < 0;
}
};
std::map<std::string, int, comp> st;
Edit :
If you're not able to use stricmp or strcasecmp use :
#include<algorithm>
//...
string tolower(string s) {
std::transform(s.begin(), s.end(), s.begin(), ::tolower );
return s;
}
struct comp {
bool operator() (const std::string& lhs, const std::string& rhs) const {
return tolower(lhs) < tolower(rhs);
}
};
std::map<std::string, int, comp> st;

There are two ways to do this
First - change the "comparison" function to ignore case
Second - whenever you use a string to either put or get a value from the map, wrap it with a function that turns it into lowercase.
For the first all you need to do is create a "function class" (a class with operator() ) that receives two strings and returns whether the left is "smaller" than the right:
struct my_comparitor{
bool operator()(const std::string &a, const std::string &b){
// return iwhether a<b
}
};
std::map<std::string,DATA_TYPE,my_comparitor> my_map;
For the second just do this:
std::map<std::string,DATA_TYPE> my_map;
my_map.insert(std::make_pair(TO_LOWERCASE("hello"),1));
iter=my_map.find(TO_LOWERCASE(key));
cout << my_map[TO_LOWERCASE(name)];
// etc.
I'm not sure if a function that transforms to lowercase is already part of stl - but either way it's easy to write.

Related

C++ - adding string key to map with const char* key creates new element instead of modifying [duplicate]

This question already has answers here:
Using char* as a key in std::map
(10 answers)
Error trying to find const char* key from std::map
(2 answers)
Using const char* as key for map/unordered_map
(3 answers)
Pointers as keys in map C++ STL
(5 answers)
Closed last month.
I have a simple map with const char* key and bool value, and I have preadded keys, but when I try to modify a value with a string, it creates a new entry, not edits an existing entry, and I have both with same key name.
map<const char*, bool> test=
{
{"Test", false},
{"test2", false}
};
string s = "Test";
test[s.c_str()] = true;
Gives me map test with
{"Test", false},
{"test2", false},
{"Test", false;}
If you really want have C strings as keys you need to provide a user defined comparator since otherwise the map will compare the actual pointer values, not the C strings they point at.
Example:
#include <cstring>
#include <iostream>
#include <map>
#include <string>
// a comparator class for C strings
struct cstring_less {
bool operator()(const char* lhs, const char* rhs) const {
return std::strcmp(lhs, rhs) < 0;
}
};
int main() {
// supply the comparator as the third template parameter:
std::map<const char*, bool, cstring_less> test = {
{"Test", false},
{"test2", false}
};
std::string s = "Test";
test[s.c_str()] = true;
for(auto&[k,v] : test) {
std::cout << k << ' ' << v << '\n';
}
}
I really suggest that you use std::string as Key though.
The current map can't be used if any of the C strings (that are not string literals) you store pointers to have gone out of scope. The pointers you store are then "dangling" and dereferencing them would make the program have undefined behavior.

sorting a string vector based on the string size [duplicate]

This question already has answers here:
sorting vector of vector of strings in C++
(4 answers)
Closed 9 years ago.
I wanted to know how I can sort a string vector such that the string with the least amount of characters is on top of the vector. For instance if the vector has ABCD,ABCDE,ABC in it. ABC gets to the top.I would be interested to know how this could be achieved with sort_if and what the predicate would look like ? Any other methods are also welcome
Make your own custom functor to compare the size of string(s) and use that to sort the strings.
struct compare {
inline bool operator()(const std::string& first,
const std::string& second) const
{
return first.size() < second.size();
}
};
std::vector<std::string> v;
compare c;
std::sort(v.begin(), v.end(), c);
In modern c++ we can use a lambda to do the same
std::vector<std::string> v;
std::sort(v.begin(), v.end(), []
(const std::string& first, const std::string& second){
return first.size() < second.size();
});
Should be able to use regular std::sort(first, last, compare), and a compare function like this:
bool compareLen(const std::string& a, const std::string& b)
{
return (a.size() < b.size());
}
std::sort takes an optional argument for a custom comparison
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
You can just define a function that compares based on the length.

Counting matches in vector of structs

I have a problem that requires me to count the number of instances within this array that uses either std::count() or std::find(). I'm aware of how to do this using a standard data (see bottom code) type but not with the NameContainer that I'm using.
//Type
struct NameContainer{
char name [32];
}
//An array of containers
NameContainer *_storedNames = new NameContainer[_numberOfNames];
//An example of what I'm trying to do with a string rather than the NameContainer
std::vector<string> v(_storedNames, _storedNames + _numberOfNames);
//returns an numeric value
return std::count(v.begin(), v.end(), nameToSearch))
You can use a functor
struct names_equal {
string comp_to;
names_equal(string a) : comp_to(a) {}
bool operator()(NameContainer& p) {
return p.name == comp_to;
}
};
And count like
cout << std::count_if(v.begin(), v.end(), names_equal(nameToSearch));
This way nameToSearch doesn't have to be hard coded.
EDIT
If you can not use count_if, and has to be count then modify NameContainer and overload == for it.
struct NameContainer{
string name;
bool operator==(string str) {
return name == str;
}
};
Then count like this
cout << std::count(v.begin(), v.end(), nameToSearch);
you can use count_if and you provide a predicate (Unary function that accepts an element in the range as argument, and returns a value convertible to bool)
for example
bool myPred(NameContainer n){
return (strcmp(n.name, "name") == 0); }
std::vector<NameContainer> v(_storedNames, _storedNames + _numberOfNames);
int i=std::count_if(v.begin(), v.end(), myPred))
you can use strcmp() to compare character arrays.
if using only std::count or std::find:
both count and find takes the same type argument to compare as the type of conatainer, in your case NameContainer. std::count will execute following to compare searched values:
if (*first == val)
what means you have to overload operator== taking your class as arguments.
inline bool operator == (const NameContainer &first,const NameContainer &second){
return (strcmp(first.name,second.name)==0);
}
and then call std::count(v.begin(), v.end(), myObjectPredicate))
with myObjectPredicate being your NameContainer class object with name to be searched in vector.
so here is working solution. you might improve it in details:
struct NameContainer{
char name [32];
};
inline bool operator== (const NameContainer &first,const NameContainer &second){
return (strcmp(first.name,second.name)==0);
}
int main(int argc, char** argv) {
NameContainer* _storedNames = new NameContainer[1];
std::vector<NameContainer> vn(_storedNames, _storedNames + 1);
const char* cc="piotr";
NameContainer nc;
memcpy(nc.name,cc,strlen(cc)+1);
vn.push_back(nc);
NameContainer myObjectPredicate;
memcpy(myObjectPredicate.name,cc,strlen(cc)+1);
int count=std::count(vn.begin(), vn.end(), myObjectPredicate);
std::cout<<count;
return 2400;
}
output:
1
Read the docs on std::count, you'll see that it uses operator== for it's comparisons. Therefore, if you want to use std::count, the thing you want to compare MUST have an operator== defined for it. In your case, you could add one to your NameContainer pretty easily.

Sorting a vector<Struct> alphabetically [duplicate]

This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 6 years ago.
I have a std::vector<Word> data that is off of the struct below:
struct Word
{
std::string word;
int line_number;
};
I have read in words from a file and pushed it in to my vector storing the words in the string above along with the line number that the word appears on. Now I need to sort the words alphabetically and I attempt the following:
std::sort(data.begin(), data.end());
However when I try to compile the following I get a crazy long list of errors. I believe this is due to the sort algorithm trying to compare the vector.begin() to vector.end() but it doesn't know how to evaluate the struct word to another struct word.
However neither do I. I am stumped on how to compare the string contained with the structs in the vector.
In this scenario you should write a function that compares two Word structs and pass that function to std::sort.
bool compare_by_word(const Word& lhs, const Word& rhs) {
return lhs.word < rhs.word;
}
std::sort(data.begin(), data.end(), compare_by_word);
In this question you can find solution if you want to write a generic comparator for comparing objects based on an attribute.
Update Since we've had C++11 and C++14 for a while now, I'm adding a solution using a lambda, because that is probably the better practice now:
std::sort(data.begin(), data.end(), [](const Word& lhs, const Word& rhs) {
return lhs.word < rhs.word;
});
you should implement operator< to your struct Word
Instead of sorting the vector afterward, you can also use a container which stores its items in a sorted manner.
#include <string>
#include <set>
#include <map>
struct Word
{
std::string word;
int line_number;
};
struct compare_by_word
{
bool operator()(const Word& lhs, const Word& rhs)
{
return lhs.word < rhs.word;
}
};
std::set<Word, compare_by_word> foo;
std::map<std::string, int> bar;
If your compiler supports lamda expressions you could just add one as the compare function.
std::sort(data.begin(), data.end(),
[](const Word & lhs, const Word & rhs)
{
return lhs.word < rhs.word;
});

sorting a vector of structs [duplicate]

This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 9 years ago.
I have a vector<data> info where data is defined as:
struct data{
string word;
int number;
};
I need to sort info by the length of the word strings. Is there a quick and simple way to do it?
Use a comparison function:
bool compareByLength(const data &a, const data &b)
{
return a.word.size() < b.word.size();
}
and then use std::sort in the header #include <algorithm>:
std::sort(info.begin(), info.end(), compareByLength);
Just make a comparison function/functor:
bool my_cmp(const data& a, const data& b)
{
// smallest comes first
return a.word.size() < b.word.size();
}
std::sort(info.begin(), info.end(), my_cmp);
Or provide an bool operator<(const data& a) const in your data class:
struct data {
string word;
int number;
bool operator<(const data& a) const
{
return word.size() < a.word.size();
}
};
or non-member as Fred said:
struct data {
string word;
int number;
};
bool operator<(const data& a, const data& b)
{
return a.word.size() < b.word.size();
}
and just call std::sort():
std::sort(info.begin(), info.end());
Yes: you can sort using a custom comparison function:
std::sort(info.begin(), info.end(), my_custom_comparison);
my_custom_comparison needs to be a function or a class with an operator() overload (a functor) that takes two data objects and returns a bool indicating whether the first is ordered prior to the second (i.e., first < second). Alternatively, you can overload operator< for your class type data; operator< is the default ordering used by std::sort.
Either way, the comparison function must yield a strict weak ordering of the elements.
As others have mentioned, you could use a comparison function, but you can also overload the < operator and the default less<T> functor will work as well:
struct data {
string word;
int number;
bool operator < (const data& rhs) const {
return word.size() < rhs.word.size();
}
};
Then it's just:
std::sort(info.begin(), info.end());
Edit
As James McNellis pointed out, sort does not actually use the less<T> functor by default. However, the rest of the statement that the less<T> functor will work as well is still correct, which means that if you wanted to put struct datas into a std::map or std::set this would still work, but the other answers which provide a comparison function would need additional code to work with either.