Як відсортувати вектор STL?


76

Я хотів би відсортувати vector

vector<myClass> object;

Де myclassмістить багато intзмінних. Як я можу відсортувати своє vectorза будь-якою конкретною змінною даних myClass.

Відповіді:


80

Перевантажте менше, ніж оператор, потім сортуйте. Це приклад, який я знайшов в Інтернеті ...

class MyData
{
public:
  int m_iData;
  string m_strSomeOtherData;
  bool operator<(const MyData &rhs) const { return m_iData < rhs.m_iData; }
};

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

Джерело: тут


14
Ви захочете зробити op <() const і передати його параметр як посилання на const.

19
@Neil, я розмістив приклад, який знайшов, бо не встиг це все набрати, чувак. Це було вагомим прикладом і вирішило проблему. Я радий, що вам знадобилося 40 хвилин, щоб прийняти рішення про голосування. Я міг бачити, що за нього голосували проти, якщо я не включив вихідний сайт, але я зробив. Це не так, як я намагався закласти його як свій власний.
Гейб,

9
@Neil Я визнаю, що минув деякий час з того часу, як я використовував c ++, але я згадав деякі загальні ідеї з цим запитанням, тому я відповів. Я не стверджую, що він ідеальний, але він працює, я спробував сам. Я взяв вашу пропозицію і додав її. Якщо у вас є якісь інші проблеми з цим, говоріть замість цього, діючи так поблажливо. Поводитися так, як це не було SO, це приблизно будь-який чувак.
Гейб,

4
Якщо ви спробували, і він "працює", ваш компілятор зламаний. І, будь ласка, не називайте мене "чувак".

2
@gmcalab Ваше нерозуміння того, що Ніл мав на увазі під "make op <() const", демонструє, що ви насправді не розумієте розміщений вами код. Те, що щось "працює" для вас, не означає, що це правильно робити.
Тайлер Макгенрі,

117
std::sort(object.begin(), object.end(), pred());

де, pred()- об'єкт функції, що визначає порядок на об'єктах myclass. Крім того, ви можете визначити myclass::operator<.

Наприклад, ви можете пройти лямбду:

std::sort(object.begin(), object.end(),
          [] (myclass const& a, myclass const& b) { return a.v < b.v; });

Або якщо ви застрягли в C ++ 03, підхід до функціонального об'єкта ( vє елементом, за яким ви хочете сортувати):

struct pred {
    bool operator()(myclass const & a, myclass const & b) const {
        return a.v < b.v;
    }
};

@NativeCoder - це те, для чого призначений оператор - ви можете визначити його як завгодно і відповідно до будь-якої змінної, яку ви хочете. це називається Operator Overloading cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html .
Амір Рахум,

8
Підхід предикатів є набагато кращим, ніж підхід перевантаження оператора, якщо у вас немає загального впорядкування цього конкретного класу, а просто ви хочете відсортувати його для цього вектора.
Matthieu M.

15

Вказівник на учасника дозволяє написати єдиний компаратор, який може працювати з будь-яким членом даних вашого класу:

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

template <typename T, typename U>
struct CompareByMember {
    // This is a pointer-to-member, it represents a member of class T
    // The data member has type U
    U T::*field;
    CompareByMember(U T::*f) : field(f) {}
    bool operator()(const T &lhs, const T &rhs) {
        return lhs.*field < rhs.*field;
    }
};

struct Test {
    int a;
    int b;
    std::string c;
    Test(int a, int b, std::string c) : a(a), b(b), c(c) {}
};

// for convenience, this just lets us print out a Test object
std::ostream &operator<<(std::ostream &o, const Test &t) {
    return o << t.c;
}

int main() {
    std::vector<Test> vec;
    vec.push_back(Test(1, 10, "y"));
    vec.push_back(Test(2, 9, "x"));

    // sort on the string field
    std::sort(vec.begin(), vec.end(), 
        CompareByMember<Test,std::string>(&Test::c));
    std::cout << "sorted by string field, c: ";
    std::cout << vec[0] << " " << vec[1] << "\n";

    // sort on the first integer field
    std::sort(vec.begin(), vec.end(), 
        CompareByMember<Test,int>(&Test::a));
    std::cout << "sorted by integer field, a: ";
    std::cout << vec[0] << " " << vec[1] << "\n";

    // sort on the second integer field
    std::sort(vec.begin(), vec.end(), 
        CompareByMember<Test,int>(&Test::b));
    std::cout << "sorted by integer field, b: ";
    std::cout << vec[0] << " " << vec[1] << "\n";
}

Вихід:

sorted by string field, c: x y
sorted by integer field, a: y x
sorted by integer field, b: x y

Привіт, Стів, я думав про вирішення тієї ж проблеми, що і це питання, без особливого прогресу! Ваше рішення мені дуже добре виглядає. Думаю, мені знадобилося б багато часу, щоб це придумати, якщо взагалі коли-небудь! Я прочитав ефективні C ++ та Effective STL Майерса та загальні знання C ++ Dewhurst. Цікаво, чи не могли б Ви порекомендувати ще прочитати, чи знаєте Ви якусь книгу, яка зокрема охоплює приклади, подібні до Вашої вище, чи відсутні будь-які загальні поради, які допоможуть мені побачити подібні рішення?
Paul Caheny

1
@Czarak: переглянувши це ще раз, можливо, його можна було б покращити. Наприклад, він може оптимізувати краще , якщо покажчик на член був параметр шаблону: template <typename T, typename U, U (T::*F)> struct CompareByMember2 { bool operator()(const T &lhs, const T & rhs) { return lhs.*F < rhs.*F; }};. Чи можливо це залежить, чи використовує виклик змінну, за якою член сортує, або різні абоненти вказують різних конкретних членів.
Steve Jessop

@Czarak: що стосується читання, я не впевнений. Тут «фішка» полягає у поєднанні покажчиків учасників із шаблонами, я думаю, що це питання того, щоб зручно писати шаблони та знати, що з ними можна зробити. Книга Вандеворда та Джосуттіса "Шаблони C ++ - Повний посібник" повинна бути доброю, але я її не читав. Це може бути досить дорослим і досить дорогим, що на сьогоднішній день є кращий варіант. Подивіться на stackoverflow.com/questions/388242/… . І зауважте, що якщо у вас є C ++ 0x, лямбда-функція може перемогти написання для цього всього класу!
Steve Jessop

Привіт, Стів, дякую за дуже корисні відповіді. Я розглядав лямбда-функції, як ви запропонували, це призвело мене до іншої проблеми, яку, я вважаю, я б також мав, якби хотів знайти рішення, подібне до того, що ви запропонували у вашій відповіді вище. Якщо ви можете приділити час, можливо, ви могли б подивитися?stackoverflow.com/questions/4268848 / ...
Paul Caheny

Я з'ясував, що коли ви надаєте функцію, яка створює екземпляр шаблонного об'єкта, виклична функція може бути зведена до такого "sort (begin (vec), end (vec), by_member (& Test :: c));"
Арне

9

Як пояснено в інших відповідях, вам потрібно надати функцію порівняння. Якщо ви хочете зберегти визначення цієї функції близько до sort виклику (наприклад, якщо це має сенс лише для такого сортування), ви можете визначити його тут boost::lambda. Використовуйте boost::lambda::bindдля виклику функції-члена.

Наприклад, щоб сортувати за змінною-членом або функцією data1:

#include <algorithm>
#include <vector>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
using boost::lambda::bind;
using boost::lambda::_1;
using boost::lambda::_2;

std::vector<myclass> object(10000);
std::sort(object.begin(), object.end(),
    bind(&myclass::data1, _1) < bind(&myclass::data1, _2));

2

це мій підхід до вирішення цього загалом. Він розширює відповідь Стіва Джессопа, вилучаючи вимогу явно встановлювати аргументи шаблону та додаючи опцію також використовувати функтоїни та покажчики на методи (геттери)

#include <vector>
#include <iostream>
#include <algorithm>
#include <string>
#include <functional>

using namespace std;

template <typename T, typename U>
struct CompareByGetter {
    U (T::*getter)() const;
    CompareByGetter(U (T::*getter)() const) : getter(getter) {};
    bool operator()(const T &lhs, const T &rhs) {
        (lhs.*getter)() < (rhs.*getter)();
    }
};

template <typename T, typename U>
CompareByGetter<T,U> by(U (T::*getter)() const) {
    return CompareByGetter<T,U>(getter);
}

//// sort_by
template <typename T, typename U>
struct CompareByMember {
    U T::*field;
    CompareByMember(U T::*f) : field(f) {}
    bool operator()(const T &lhs, const T &rhs) {
        return lhs.*field < rhs.*field;
    }
};

template <typename T, typename U>
CompareByMember<T,U> by(U T::*f) {
    return CompareByMember<T,U>(f);
}

template <typename T, typename U>
struct CompareByFunction {
    function<U(T)> f;
    CompareByFunction(function<U(T)> f) : f(f) {}
    bool operator()(const T& a, const T& b) const {
        return f(a) < f(b);
    }
};

template <typename T, typename U>
CompareByFunction<T,U> by(function<U(T)> f) {
    CompareByFunction<T,U> cmp{f};
    return cmp;
}

struct mystruct {
    double x,y,z;
    string name;
    double length() const {
        return sqrt( x*x + y*y + z*z );
    }
};

ostream& operator<< (ostream& os, const mystruct& ms) {
    return os << "{ " << ms.x << ", " << ms.y << ", " << ms.z << ", " << ms.name << " len: " << ms.length() << "}";
}

template <class T>
ostream& operator<< (ostream& os, std::vector<T> v) {
    os << "[";
    for (auto it = begin(v); it != end(v); ++it) {
        if ( it != begin(v) ) {
            os << " ";
        }
        os << *it;
    }
    os << "]";
    return os;
}

void sorting() {
    vector<mystruct> vec1 = { {1,1,0,"a"}, {0,1,2,"b"}, {-1,-5,0,"c"}, {0,0,0,"d"} };

    function<string(const mystruct&)> f = [](const mystruct& v){return v.name;};

    cout << "unsorted  " << vec1 << endl;
    sort(begin(vec1), end(vec1), by(&mystruct::x) );
    cout << "sort_by x " << vec1 << endl;
    sort(begin(vec1), end(vec1), by(&mystruct::length));
    cout << "sort_by len " << vec1 << endl;
    sort(begin(vec1), end(vec1), by(f) );
    cout << "sort_by name " << vec1 << endl;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.