Сортировка вектора объектов по свойству объекта - PullRequest
10 голосов
/ 03 марта 2011

Я работаю над проектом для школы и мне нужно отсортировать некоторые данные.Мне дали вектор объектов, и я должен отсортировать объекты (либо на месте, либо с помощью индекса) на основе одного из их свойств.Есть несколько разных объектов и несколько разных свойств, по которым они могут быть отсортированы.Какой лучший способ сделать это?

Ответы [ 4 ]

17 голосов
/ 03 марта 2011

Используйте std::sort и функтор.Например:

struct SortByX
{
   bool operator() const (MyClass const & L, MyClass const & R) { return L.x < R.x; }
};

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

Оператор функтора () должен вернуть true, если L меньше R для требуемого порядка сортировки.

13 голосов
/ 03 марта 2011

Есть несколько разных объектов и несколько разных свойств, по которым можно было бы их отсортировать.

Хотя решение, опубликованное Эриком, является правильным, это утверждение заставляет меня думать, что в лучшем случае нецелесообразно, если вы на самом деле планируете сортировать по нескольким открытым элементам данных нескольких классов несколькими способами в одной программе в качестве каждого метода сортировки. потребуется собственный тип функтора.

Я рекомендую следующую абстракцию:

#include <functional>

template<typename T, typename M, template<typename> class C = std::less>
struct member_comparer : std::binary_function<T, T, bool>
{
    explicit member_comparer(M T::*p) : p_(p) { }

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

private:
    M T::*p_;
};

template<typename T, typename M>
member_comparer<T, M> make_member_comparer(M T::*p)
{
    return member_comparer<T, M>(p);
}

template<template<typename> class C, typename T, typename M>
member_comparer<T, M, C> make_member_comparer2(M T::*p)
{
    return member_comparer<T, M, C>(p);
}

Использование будет выглядеть так:

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

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.push_back(MyClass(2, "two"));
    vec.push_back(MyClass(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_comparer2<std::greater>(&MyClass::s));
}

Это будет работать для любого типа с общедоступными членами данных и сэкономит много печатания, если вам нужно сортировать свои классы несколькими способами.

Вот вариант, который работает с общедоступными функциями-членами вместо общедоступных элементов данных:

#include <functional>

template<typename T, typename M, template<typename> class C = std::less>
struct method_comparer : std::binary_function<T, T, bool>
{
    explicit method_comparer(M (T::*p)() const) : p_(p) { }

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

private:
    M (T::*p_)() const;
};

template<typename T, typename M>
method_comparer<T, M> make_method_comparer(M (T::*p)() const)
{
    return method_comparer<T, M>(p);
}

template<template<typename> class C, typename T, typename M>
method_comparer<T, M, C> make_method_comparer2(M (T::*p)() const)
{
    return method_comparer<T, M, C>(p);
}

С использованием как:

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

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.push_back(MyClass(2, "two"));
    vec.push_back(MyClass(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_comparer2<std::greater>(&MyClass::s));
}
3 голосов
/ 03 октября 2013

Вот мой вариант ответа, просто используйте лямбда-функцию! Работает, использует меньше кода и, на мой взгляд, элегантно!

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

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.push_back(MyClass(2, "two"));
    vec.push_back(MyClass(8, "eight"));

    // sort by i, ascending
    std::sort(vec.begin(), vec.end(), [](MyClass a, MyClass b){ return a.i < b.i; });
    // sort by s, ascending
    std::sort(vec.begin(), vec.end(), [](MyClass a, MyClass b){ return a.s < b.s; });
    // sort by s, descending
    std::sort(vec.begin(), vec.end(), [](MyClass a, MyClass b){ return a.s > b.s; });
}
1 голос
/ 03 марта 2011

РЕДАКТИРОВАТЬ: замена на lambda_compare, потому что я мазохист:

Вы также можете просто создать помощника, который позволит вам указать, какое значение использовать через лямбда-выражение:

template <class F> class lambda_compare
{
public:
    lambda_compare(F f_): f(f_) { }
    template <class T> bool operator()(const T &lhs, const T &rhs) const
    { return f(lhs)<f(rhs); }

private:
    F f;
};

template <class F> lambda_compare<F> make_lambda_compare(F f)
{ return f; }

...
std::sort(vec.begin(), vec.end(), make_lambda_compare([](const foo &value) { return value.member; }));
...