Sorting a Vector of Objects by a Property of the Object

Sort a vector of objects by an object's attribute

You should implement an operator< on cat so that cats can be sorted:

class cat {
public:
int age;
bool operator< (const cat &other) const {
return age < other.age;
}
};

You can then include the "algorithm" header and use std::sort on your array:

vector< cat > catSorter::SortCatsByAge(){
vector< cat > cats_copy = cats;
std::sort(cats_copy.begin(), cats_copy.end());
return cats_copy;
}

Sorting a vector of objects by a property of the object

There are several different objects and several different properties that could it be sorted by.

While the solution Erik posted is correct, this statement leads me to think that it's impractical at best if you are in fact planning to sort by multiple public data members of multiple classes in multiple ways in the same program, as each sorting method will require its own functor type.

I recommend the following abstraction:

#include <functional>

template<typename C, typename M, template<typename> class Pred = std::less>
struct member_comparer : std::binary_function<C, C, bool> {
explicit member_comparer(M C::*ptr) : ptr_{ptr} { }

bool operator ()(C const& lhs, C const& rhs) const {
return Pred<M>{}(lhs.*ptr_, rhs.*ptr_);
}

private:
M C::*ptr_;
};

template<template<typename> class Pred = std::less, typename C, typename M>
member_comparer<C, M, Pred> make_member_comparer(M C::*ptr) {
return member_comparer<C, M, Pred>{ptr};
}

Usage would look like:

#include <algorithm>
#include <string>
#include <vector>

struct MyClass {
int i;
std::string s;

MyClass(int i_, std::string const& s_) : i{i_}, s{s_} { }
};

int main() {
std::vector<MyClass> vec;
vec.emplace_back(2, "two");
vec.emplace_back(8, "eight");

// sort by i, ascending
std::sort(vec.begin(), vec.end(), make_member_comparer(&MyClass::i));
// sort by s, ascending
std::sort(vec.begin(), vec.end(), make_member_comparer(&MyClass::s));
// sort by s, descending
std::sort(vec.begin(), vec.end(), make_member_comparer<std::greater>(&MyClass::s));
}

This will work for any type with public data members, and will save a lot of typing if you need to sort your classes more than a couple of different ways.

Here is a variation that works with public member functions instead of public data members:

#include <functional>

template<typename C, typename M, template<typename> class Pred = std::less>
struct method_comparer : std::binary_function<C, C, bool> {
explicit method_comparer(M (C::*ptr)() const) : ptr_{ptr} { }

bool operator ()(C const& lhs, C const& rhs) const {
return Pred<M>{}((lhs.*ptr_)(), (rhs.*ptr_)());
}

private:
M (C::*ptr_)() const;
};

template<template<typename> class Pred = std::less, typename C, typename M>
method_comparer<C, M, Pred> make_method_comparer(M (C::*ptr)() const) {
return method_comparer<C, M, Pred>{ptr};
}

With usage like:

#include <algorithm>
#include <string>
#include <vector>

class MyClass {
int i_;
std::string s_;

public:
MyClass(int i, std::string const& s) : i_{i}, s_{s} { }

int i() const { return i_; }
std::string const& s() const { return s_; }
};

int main() {
std::vector<MyClass> vec;
vec.emplace_back(2, "two");
vec.emplace_back(8, "eight");

// sort by i(), ascending
std::sort(vec.begin(), vec.end(), make_method_comparer(&MyClass::i));
// sort by s(), ascending
std::sort(vec.begin(), vec.end(), make_method_comparer(&MyClass::s));
// sort by s(), descending
std::sort(vec.begin(), vec.end(), make_method_comparer<std::greater>(&MyClass::s));
}

Sorting a vector of custom objects

A simple example using std::sort

struct MyStruct
{
int key;
std::string stringValue;

MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};

struct less_than_key
{
inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
};

std::vector < MyStruct > vec;

vec.push_back(MyStruct(4, "test"));
vec.push_back(MyStruct(3, "a"));
vec.push_back(MyStruct(2, "is"));
vec.push_back(MyStruct(1, "this"));

std::sort(vec.begin(), vec.end(), less_than_key());

Edit: As Kirill V. Lyadvinsky pointed out, instead of supplying a sort predicate, you can implement the operator< for MyStruct:

struct MyStruct
{
int key;
std::string stringValue;

MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}

bool operator < (const MyStruct& str) const
{
return (key < str.key);
}
};

Using this method means you can simply sort the vector as follows:

std::sort(vec.begin(), vec.end());

Edit2: As Kappa suggests you can also sort the vector in the descending order by overloading a > operator and changing call of sort a bit:

struct MyStruct
{
int key;
std::string stringValue;

MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}

bool operator > (const MyStruct& str) const
{
return (key > str.key);
}
};

And you should call sort as:

std::sort(vec.begin(), vec.end(),greater<MyStruct>());

Sorting vector of objects by object's variable

You can try something like that:

for (int i = 0; i < myVector.size(); i++)
{
for (int j = 0; j < myVector.size() - 1; j++)
{
if (myVector[j].x < myVector[j + 1].x)
{
std::swap(myVector[j], myVector[j + 1]);
}
else if (myVector[j].x == myVector[j + 1].x)
{
if (myVector[j].y < myVector[j + 1].y)
std::swap(myVector[j], myVector[j + 1]);
}
}
}

sorting a vector of classes based on a variable in the class

If you have a vector of your class object

std::vector<MyClass> objs;

And the variable to sort by is

MyClass.value

Then you can

std::sort(objs.begin(),
objs.end(),
[](const MyClass& lhs, const MyClass& rhs)
{
return lhs.value < rhs.value;
});

Sort vector of class objects based on some member variable

You can implement the comparison operators.

bool Record::operator<(const Record& rhs) {
return score < rhs.score;
}

bool Record::operator<=(const Record& rhs) {
return score <= rhs.score;
}

Note, if you define < and <=, you should also probably define the other comparison operators >, >=, ==, and !=. You should also enforce a strict weak ordering (e.g. a < b, b < c implies a < c).

Then to sort descending, you could use the following...

std::sort(records.begin(), records.end(), 
[] (const Record& lhs, const Record& rhs) -> bool { return lhs > rhs; });

C++11 sort collection of custom objects by several properties

Just change the comparator. Suppose you want to order by year, then by month, then by amount, then:

std::sort( values.begin(), values.end(), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
return std::tie(lhs.year, lhs.month, lhs.amount) <
std::tie(rhs.year, rhs.month, rhs.amount);
});

std::tuple's operator< does a lexicographical comparison, so there's no need to roll your own (and risk getting it wrong). To do a descending order for an attribute, just swap lhs and rhs for that attribute.

std::sort on a vector of Objects containing Objects* pointers

Yes, the pointers will still point to the same instance. But that may not be what you actually want. When you call std::sort on a vector, it does not change the location of instances (that's not even a thing you can do in C++). It swaps their values with each other. This is probably better explained with a simplified example. Take this struct:

struct Foo
{
int x;
Foo* buddy;
};

bool operator<(Foo const& lhs, Foo const& rhs)
{
return lhs.x < rhs.x;
}

Now, lets say I make a vector of these, with decreasing x values, and each one having a pointer to the one that comes after it (and the last having a pointer to the first):

std::vector<Foo> vec(5);
for (int i = 0; i < 5; ++i)
{
vec[i].x = 4 - i;
vec[i].buddy = &vec[(i + 1) % 5];
}

Now, if I sort this vector, it is essentially going to reverse. But the instances themselves don't change their location. Instead, their values change such that the first one has the lowest x value, and it increases from there. Now, the pointer member changes along with the x value.

So take, for example, vec[0], which had an x value of 4, and a pointer to vec[1] (which has an x value of 3). After the sort, those values will end up in the last element, vec[4]. So vec[4] will have an x value of 4, and a pointer to vec[1] (which, after the sort, has an x value of 1).

So, before the sort, the element with the x value 4, had a pointer to the element with an x value of 3. After the sort, the element with the x value of 4 has a pointer to the element with the x value of 1. I doubt this is what you wanted.

Since you are interested in the identity of the instances, not just their values, you should probably be using something like std::vector<std::unique_ptr<dockPose>>. For my Foo example, I could do something like this:

std::vector<std::unique_ptr<Foo>> vec;
for (int i = 0; i < 5; ++i)
vec.emplace_back(new Foo);
for (int i = 0; i < 5; ++i)
{
vec[i]->x = 4 - i;
vec[i]->buddy = vec[(i + 1) % 5].get();
}

Note that when you do this, you need to use a different comparison function. One which compares the dereferenced pointers, rather than the pointers themselves.

c++ std::sort on vector of objects by attribute

Try this:

[] (const finalwords& x, const finalwords& y)
{
if (x.isPopular != y.isPopular) return x.isPopular;
else if (x.isFirstSearch != y.isFirstSearch) return x.isFirstSearch;
else return false;
}

DEMO



Related Topics



Leave a reply



Submit