#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());
Related
I have a:
std::map<long, std::wstring> fNames; // ex: fNames[FileReferenceNumber] = L"hello.txt"
As std::map has keys ordered, but not values (a hash-table unordered_map has even nothing ordered), I'd like to create a vector:
std::vector<long> v;
that would contain the keys to allow an iteration of fNames sorted by values.
Example: if we have
9187 => "hello.txt"
13 => "z.txt"
1777 => "a.txt"
Then v would be: [1777, 9187, 13], allowing an iteration of fNames sorted by values:
for (int i = 0; i < v.size(); i++)
wcout << fNames[v[i]]; // a.txt, hello.txt, z.txt
Is there a way to create this vector v by using std::sort? I don't see how. Should I use a custom predicate?
PS: Even better: would it be possible to produce a std::vector<wstring> w sorted? i.e. each element w[0], w[1], etc. would contain a "link" (pointer?) to fNames's wstrings, but not a copy (to avoid duplicating the string data)?
Or maybe it would be a std::vector<wchar_t *> w?
PS: Even better: would it be possible to produce a
std::vector<wstring> w sorted? i.e. each element w[0], w[1], etc.
would contain a "link" (pointer?) to fNames's wstrings, but not a copy
(to avoid duplicating the string data)?
A vector<wstring> would contain duplicates of the strings (as CoW - Copy on Write - has been forbidden for std::[w]string since C++11). If you want to use const wchar_t* to avoid string duplicates, you can do something like this:
vector<const wchar_t*> sortedFilenames;
// Reserve room in the vector, since we know how many strings to add
sortedFilenames.reserve(fNames.size());
// Add string pointers from map to vector
for (const auto& e : fNames) {
// Avoid duplicates using const wchar_t*
sortedFilenames.push_back(e.second.c_str());
}
// Sort the string vector
sort(begin(sortedFilenames), end(sortedFilenames),
[](const auto& p1, const auto& p2) {
// Use whatever sorting rule you need here...
return wcscmp(p1, p2) < 0;
}
);
EDIT As per your comment, you can use vector<const wstring*> as well, e.g.:
vector<const wstring*> sortedFilenames;
// Reserve room in the vector, since we know how many strings to add
sortedFilenames.reserve(fNames.size());
// Add string pointers from map to vector
for (const auto& e : fNames) {
sortedFilenames.push_back(&(e.second));
}
// Sort the string vector
sort(begin(sortedFilenames), end(sortedFilenames),
[](const auto& p1, const auto& p2) {
return (*p1) < (*p2); // or whatever sorting rule...
}
);
Use a vector of pointers to the elements of the map, and sort those pointers. This gives you access to both the longs and the wstrings, without copying.
using value_type = std::map<long, std::wstring>::value_type;
std::vector<value_type*> v;
for (auto& e : fNames)
v.push_back(&e);
auto compare_by_second = [](value_type* lhs, value_type* rhs) {
return lhs->second < rhs->second;
};
std::sort(v.begin(), v.end(), compare_by_second);
Now you have access to the wstring values through v[N]->second, and the long values through v[N]->first.
Pre-C++11 version
bool compare_by_second(std::pair<const long, std::wstring>* lhs,
std::pair<const long, std::wstring>* rhs)
{
return lhs->second < rhs->second;
}
// in some function
std::map<long, std::wstring> fNames;
...
std::vector<std::pair<const long, std::wstring>*> v;
for (std::map<long, std::wstring>::iterator b = fNames.begin();
b != fNames.end(); ++b)
{
v.push_back(&*b);
}
std::sort(v.begin(), v.end(), compare_by_second);
If you only need the strings, and not the longs, then you can just use a vector of pointers to strings instead.
bool deref_compare(std::wstring* lhs, std::wstring* rhs)
{
return *lhs < *rhs;
}
// in some function
std::map<long, std::wstring> fNames;
...
std::vector<std::wstring*> v;
for (std::map<long, std::wstring>::iterator b = fNames.begin();
b != fNames.end(); ++b)
{
v.push_back(&b->second);
}
std::sort(v.begin(), v.end(), deref_compare);
I will solve this in c++14, because it is simpler there than c++11 or c++03, and every major compiler vendor has enough support for this to compile:
Make a vector of pointers-to-string and reserve enough space (for efficiency reasons).
std::vector<std::wstring const*> sorted_strings;
sorted_strings.reserve(fNames.size());
Populate the pointers:
for (auto& entry:fNames)
sorted_strings.push_back( &entry.second );
Sort them:
std::sort(
begin(sorted_strings), end(sorted_strings),
[](auto* lhs, auto* rhs) { return *lhs < *rhs; }
);
for a vector of keys:
std::vector<int> sorted_keys;
sorted_keys.reserve(fNames.size());
for (auto&& entry:fNames)
sorted_strings.push_back( entry.first );
std::sort(
begin(sorted_keys), end(sorted_keys),
[&](int lhs, int rhs) { return fNames[lhs] < fNames[rhs]; }
);
In c++17 you can do away with the .first and .second:
for (auto&[key, value]:fNames)
sorted_strings.push_back( &value );
using structured bindings.
I have the following struct
struct MyClass {
int myInt;
std::map<int, int> myMap;
};
I want to use unordered_set<MyClass*, PointedObjHash, PointedObEq> but I can't find a valid way to declare PointedObEq.
I tried
struct PointedObjHash {
size_t operator() (MyClass* const& c) const {
std::size_t seed = 0;
boost::hash_combine(seed, c->myInt);
boost::hash_combine(seed, c->myMap);
return seed;
}
and I hope it is fine, but I can't find a way to declare PointedObjEq
--- EDIT ---
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
struct MyClass {
...
...
bool operator==(MyClass* const& c) {
return this->myInt == c->myInt & this->myMap == c->myMap;
}
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
The unordered_set needs to use operator== (or PointedObjEq) to double-check the results of the hash function. The hash provides approximate equality, the equality function is used to weed out false positives.
If you've tested adding the same value to the set twice, then you've tested the equality function. To be sure, of course, you can have it print something to the console.
Since it's impossible to define an operator== function with two pointer operands, the PointedObjEq class will be necessary. Note that it takes a MyClass const * on both sides. Also, there's no need to use a reference to a pointer.
So,
struct PointedObjEq {
bool operator () ( MyClass const * lhs, MyClass const * rhs ) const {
return lhs->myInt == rhs->myInt
&& lhs->myMap == rhs->myMap;
}
};
This should do:
struct PointedObEq {
bool operator()(MyClass const * lhs, MyClass const * rhs) const {
return lhs->myInt == rhs->myInt && lhs->myMap == rhs->myMap;
}
};
The reason why your solution does not work is because you have effectively written a mechanism to compare a MyClass with a MyClass*, when you actually need something to compare a MyClass* with a MyClass*.
P.S.: My original answer passed the pointers by const&. Thinking about it, that's a strange coding style, so I changed it to pass the pointers by value.
typedef MyClass* PtrMyClass;
struct PointedObjCompare
{ // functor for operator==
bool operator()(const PtrMyClass& lhs, const PtrMyClass& rhs) const
{
// your code goes here
}
};
std::unordered_set < MyClass*, PointedObjHash, PointedObjCompare > myset;
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;
Given that I have a data structure,
struct data{
int val;
};
struct data A[LEN]; // LEN: some length.
// the below operator would be used in sorting.
bool operator < (struct data &a1, struct data &a2){
return a1.val < a2.val;
}
int main(){
// fill up A.
sort(A, A+LEN); // sort up A
/*Now I want something like this to happen ..
x = find(A, A+LEN, value); -> return the index such that A[index].val = value,
find is the stl find function ..
*/
}
How do you do that ?
And for any stl function how do you get to know which operators to override so that it works in the given condition ?
The modifications needed to find elements in such a case are pretty minimal. First, you want to make your operator< take its arguments as const references (technically not necessary for the current exercise, but something you want to do in general):
bool operator < (data const &a1, data const &a2){
return a1.val < a2.val;
}
Then (the part that really matters specifically for std::find) you also need to define an operator==:
bool operator==(data const &a, data const &b) {
return a.val == b.val;
}
Note, however, that you don't have to define this if you use a binary search instead:
auto pos = std::lower_bound(data, data+LEN, some_value);
This will just use the operator< that you'd already defined. If the items are already sorted anyway, this will usually be preferable (generally quite a bit faster unless LEN is quite small).
If you only want to make std::find work for your array of structure, you need to define operator== for struct data:
struct data
{
data(int value=0) : val(value) {}
int val;
};
bool operator==(const data& l, const data& r) { return l.val == r.val;}
auto x = find(A, A+LEN, value);
OR
auto x = find(A, A+LEN, data(value));
To get index of value in A, use std::distance
std::distance(A, x);
Note:
For more sufficent search with sorted container, use std::lower_bound, std::uppper_bound, std::binary_search instead.
auto lower = std::lower_bound(A, A+LEN, data(3));
auto upper = std::upper_bound(A, A+LEN, data(3));
Your operator< function signature better be like:
bool operator < (const data &a1, const data &a2)
// ^^^^^ ^^^^^
i have
class c1{
public:
int number;
c1()
{
number=rand()%10;
}
bool operator < (c1 *w)
{
return number < w->number;
}
};
vector<c1*> vec = { ... }
sort(vec.begin(),vec.end())
why it dosent sort ?
but if we had
bool operator < (c1 w)
{
return number < w.number;
}
and
vector<c1> vec = { ... }
it would have been sorted !
The most straightforward approach is to define a function
bool c1_ptr_less( c1 const *lhs, c1 const *rhs ) {
return lhs->something < rhs->something;
}
std::sort( vec.begin(), vec.end(), & c1_ptr_less );
What I would suggest is a generic functor to take care of all pointer arrays
struct pointer_less {
template< typename T >
bool operator()( T const *lhs, T const *rhs ) const
{ return * lhs < * rhs; }
};
std::sort( vec.begin(), vec.end(), pointer_less() );
Armed with this, define the usual c1::operator< ( const c1 & ) and likewise for other classes.
Generally, best practice is to avoid pointers entirely, including arrays of pointers.
To answer your title question, you can't.
Pointers are built-in types, you cannot override operators where all operands are built-in types.
Luckily, there's an overload of std::sort that allows you to specify a comparison function (or functor) so the operator< isn't used.
bool operator < (c1 *w) compares a c1 to a c1 * - Your sort compares a c1 * to a c1 *
You need to pass a compare function to std::sort:
bool compare_c1 (c1* x, c1* y)
{
return *x < y;
}
std::sort(v.begin(), v.end(), compare_c1);
Or if you are using GCC >= 4.5 or Visual Studio 2010 (I'm do not know sure about Intel compiler) you can use lambdas (they are part of the C++0x standard):
std::sort(v.begin(), v.end(), [] (c1* x, c1* y) { return *x < y; });
Add a external operator< and keep de original one:
bool operator<(c1* a, c1* b) { return *a < *b; }
Now sort will work on the vector.
phimuemue's answer sums it up, I'll just add that, as a workaround, you can create a wrapper class that contains only one member - a pointer to c1, and then overload its operator <. Then you could sort a vector of object of that class.
And in your example, vector<c1*> is sorted. Just not to the
criteria you seem to want: by default, sort uses
std::less<T> as the ordering criteria, and std::less<ci*>
compares the pointers (which is what you'd expect). If you
don't want the default criteria, then you have to pass a third
argument to sort, a predicate defining the ordering you want.
And of course, your member operator<(c1*) will only be called
when you compare a c1 with a ci* (and only if the c1 is an
rvalue). Such operators are very, very rare---normally, both
sides of a < operator should take the same type (and should be
const, since a < operator which modifies the values of the
objects it compares would be surprising, to say the least).