Sorting a vector<Struct> alphabetically [duplicate] - c++

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

Related

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

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.

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.

Iterating in a sorted manner over a std::vector<std::pair<T,U> > object

I am reading a object from a database of type Foo, as defined below. This object is a vector of Foo Members, where a Foo Members consists of a string id and a container object.
typedef std::pair<std::string, Container> FooMember;
typedef std::vector<FooMember> Foo;
I wish to iterate over a Foo object in its sorted form, where sorting is done with respect to the id. To do this I am using the following function to create first a sorted version of the object. As you can see, the object is sorted in a case insensitive manner. Is there a better way for me to iterate over this object compared to how I am currently doing it?
Foo sortedFoo(Foo& value) const {
Foo returnValue;
returnValue.reserve(value.size());
// use a map to sort the items
std::map<std::string, FooMember> sortedMembers;
{
Foo::iterator i = value.begin();
Foo::iterator end = value.end();
for(; i!=end; ++i) {
std::string name = i->first;
boost::algorithm::to_lower(name);
sortedMembers[name] = *i;
}
}
// convert the map to a vector of its values in sorted order
std::map<std::string, FooMember >::iterator i = sortedMembers.begin();
std::map<std::string, FooMember >::iterator end = sortedMembers.end();
for(; i!=end; ++i) {
returnValue.push_back(i->second);
}
return returnValue;
}
Yes: Copy the vector, then use std::sort with a custom comparison predicate:
struct ByIdCaseInsensitive {
bool operator ()(const FooMember& lhs, const FooMember& rhs) const {
return boost::algorithm::to_lower_copy(lhs.first) <
boost::algorithm::to_lower_copy(rhs.first);
}
};
Way more efficient than filling a map, and then copying back to a vector.
The predicate would be even better if it used a proper Unicode collation algorithm, but that isn't available in the standard library or Boost.
You can use std::sort
#include <algorithm>
bool comparator(const FooMember& i, const FooMember& j)
{
std::string str1 = i.first;
boost::algorithm::to_lower(str1);
std::string str2 = j.first;
boost::algorithm::to_lower(str2);
return (str1 < str2);
}
void sortFoo(Foo& value) {
std::sort (value.begin(), value.end(), comparator);
}
Or, you can keep Foo objects in a std::map<std::string, Foo> from the beginning so they remain always sorted.
The best way would be to use std::sort with a custom comparator for FooMembers:
bool cmp(const FooMember& lhs, const FooMember& rhs);
Foo sortedFoo(const Foo& value) const
{
Foo tmp = value;
return std::sort(tmp.begin(), tmp.end(), cmp);
}
where the comparison can be implemented with the help of std::lexicographical_compare and tolower:
#include <cctype> // for std::tolower
bool ci_cmp(char a, char b)
{
return std::tolower(a) < std::tolower(b);
}
#include <algorithm> // for std::sort, std::lexicographical_compare
bool cmp(const FooMember& lhs, const FooMember& rhs)
{
return std::lexicographical_compare(lhs.first.begin(),
lhs.first.end(),
rhs.first.begin(),
rhs.first.end(),
ci_cmp);
}
You can also use std::sort with a lambda expression:
std::sort(value.begin(), value.end(), [](const FooMember &lhs, const FooMember &rhs)
{
std::string str1 = i.first, str2 = j.first;
boost::algorithm::to_lower(str1);
boost::algorithm::to_lower(str2);
return str1 < str2;
});
Or use the version provided by erelender. It's up to you.
Semantically std::vector<std::pair<T,U> > is a std::map<T,U> (but implementations are usually different). If you can re-design Foo, you probably better do it. As side effect, you will get sorting for free.
typedef std::map<std::string, Container> Foo;

How to sort a vector <char*>?

#include <algorithm>
bool comparisonFunc(char* c1, char* c2)
{
return strcmp(c1, c2) ? 0 : 1;
}
vector<char*> myVec;
vector<char*>::iterator itr;
sort(myVec.begin(), myVec.end(), comparisonFunc)
Is that correct or is there a better way to do it?
std::sortexpects a "less than" predicate. You should implement your comparisonFunc() like this:
bool comparisonFunc(const char *c1, const char *c2)
{
return strcmp(c1, c2) < 0;
}
(Note the consts; they are important.)
Your current implementation couldn't possibly work because you just return if the values are equal or not. That information is not enough to sort - you need to know which one is smaller and which one is bigger (unless, of course, the values are equal).
With modern C++, you can define the comparison method inline:
std::vector<const char*> strings;
/* fill strings vector */
std::sort(strings.begin(), strings.end(), [](const char* lhs, const char* rhs) {
return strcmp(lhs, rhs) < 0;
});
Note that strcmp returns -1/0/+1 to indicate ordinality, hence the < 0 comparison.
I more often want to sort a vector of pointer to records than just plain C-strings...
template<>
struct std::less<const foo*>
{
bool operator()(const foo* lhs, const foo* rhs) const
{
return strcmp(lhs->key, rhs->key);
}
};
This redefines comparison of foo* such that the default comparison works in the sort. To me this style feels more declarative, the other more procedural. If you need multiple orderings this use of the default is problematic; if you want to be sure that all ordered collections of foo*s are in the same order it's good.
std::vector<foo*> db;
std::sort(db.begin(), db.end());

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.