I receive from an API a vector of Foo as follows:
std::vector<Foo> foos;
I have then written a function called
std::vector<std::string> getKeys(const std::vector<Foo>&)
which iterates over the container and plucks out a key of type std::string for each Foo object.
How would you iterate over the Foo objects in foos in sorted order, where sorting is done on the key and in a case insensitive manner. Additionally, I'd prefer not to make a sorted copy of foos since it is large in size.
Here's my attempt, which works but I'm wondering if it can be done better.
struct CaseInsensitiveComparitor {
bool operator ()(const std::pair<std::string, Foo&> lhs, const std::pair<std::string, Foo&> rhs) const {
std::string str1 = lhs.first;
boost::algorithm::to_lower(str1);
std::string str2 = rhs.first;
boost::algorithm::to_lower(str2);
return (str1 < str2);
}
};
// map key to Foo
std::vector<std::pair<std::string, Foo*> > tempFoos;
{
std::vector<std::string> keys = getKeys(foos);
std::vector<std::string>::iterator begin = keys.begin();
std::vector<std::string>::iterator i = keys.begin();
std::vector<std::string>::iterator end = keys.end();
for(;i!=end;++i)
{
tempFoos.push_back(*i, &foos[distance(begin,i)]);
}
std::sort(tempFoos.begin(), tempFoos.end(), CaseInsensitiveComparitor());
}
std::vector<Foo*> sortedFoos;
std::vector<std::pair<std::string, Foo*> >::iterator i = tempFoos.begin();
std::vector<std::pair<std::string, Foo*> >::iterator end = tempFoos.end();
for(;i!=end;++i)
{
sortedFoos.push_back(i->second);
}
Alternatively to your attempt, you may create an array of indexes
std::vector<size_t> indexes;
for (size_t i = 0; i != keys.size(); ++i) { indexes.push_back(i); }
using a comparator:
struct Comparator {
explicit Comparator(const std::vector<string>& keys) : keys(&keys) {}
bool operator ()(size_t lhs, size_t rhs) const {
std::string str1 = (*keys)[lhs];
boost::algorithm::to_lower(str1);
std::string str2 = (*keys)[rhs];
boost::algorithm::to_lower(str2);
return (str1 < str2);
}
private:
const std::vector<string>* keys;
};
sort this indexes array
std::sort(indexes.begin(), indexes.end(), Comparator(keys));
Now you can iterates foos and/or keys with the indexes indirection:
std::vector<Foo*> sortedFoos;
for (size_t i = 0; i != indexes.size(); ++i) {
sortedFoos.push_back(&foos[indexes[i]]);
}
You care currently iterating over foos three times and sorting it once. This is what will be making your algorithm less performant over large arrays. Why not change it to do the following
std::vecotr<Foo*>
called fooPtrVecstd::sort(fooPtrVec.begin(), fooPtrVec.end(), YourNewComparisonFunction())
to sort the vector of Foo*for(;i!=end;++end)
you have to increment your i not your end!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With