2010-05-03 20 views
56

chciałbym posortować vectorJak posortować wektor STL?

vector<myClass> object; 

Gdzie myclass zawiera wiele int zmienne. Jak mogę posortować moją vector na dowolnej zmiennej danych o numerze myClass.

Odpowiedz

61

Przeciążenie mniej niż operatora, a następnie sortowanie. To jest przykład znalazłem się w internecie ...

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()); 

Źródło: here

+13

Będziesz chciał utworzyć const op() i przekazać jego parametr jako odniesienie do const. –

+15

@Neil, wysłałem przykład, który znalazłem, ponieważ nie miałem czasu, aby napisać to wszystko koleś. To był solidny przykład i rozwiązał problem. Cieszę się, że zajęło ci 40 minut, aby zdecydować się na głosowanie w dół. Mogłem zobaczyć, że głosowanie nie zostanie uwzględnione, jeśli nie uwzględnię strony źródłowej, ale zrobiłem to. To nie tak, że próbowałem to zastawić jak własną. – Gabe

+7

@Neil Przyznam, że minęło trochę czasu, odkąd użyłem C++, ale przypomniałem sobie kilka ogólnych pomysłów na to pytanie, dlatego odpowiedziałem. Nie twierdzę, że jest doskonały, ale działa, sam go wypróbowałem. Wziąłem twoją sugestię i dodałem ją. Jeśli masz z tym jakiś inny problem, wypowiedz się, zachowując się tak protekcjonalnie. Postępowanie w ten sposób nie było SO dotyczy albo kolesia. – Gabe

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

gdzie pred() jest obiektem funkcja określająca kolejność na obiektach myclass. Alternatywnie możesz zdefiniować myclass::operator<.

Na przykład, można przekazać lambda:

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

Lub jeśli utkniesz z C++ 03, podejście obiekt function (v jest członkiem, na której chcesz sortować):

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

@NativeCoder to co operator jest - można go określić jednak lubisz i według dowolnej zmiennej. nazywa się Przeciążanie Operatorów http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html. –

+8

Podejście predykatu jest znacznie lepsze niż podejście przeładowania operatora, jeśli nie masz ogólnej kolejności tej klasy, ale po prostu chcesz posortować ją dla tego wektora. –

14

Wskaźnik do członka pozwala pisać jeden komparator, który może pracować z dowolnego członka danych klasie :

#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"; 
} 

wyjściowa:

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

Witam, Steve, myślałem o rozwiązaniu tego samego problemu, co to pytanie bez większego postępu! Twoje rozwiązanie wygląda dla mnie bardzo dobrze. Myślę, że zabrałoby mi to dużo czasu, jeśli kiedykolwiek! Czytałem Myers 'Effective C++ i Effective STL oraz Dewhurst's C++ Common Knowledge. Zastanawiam się, czy mógłbyś polecić trochę więcej czytania, czy znasz jakieś książki, które opisują przykłady takie jak twoja w szczególności, czy te bardziej ogólne sugestie, które pomogą mi znaleźć takie rozwiązania? –

+1

@Czarak: patrząc na to jeszcze raz, prawdopodobnie można by poprawić. Na przykład może lepiej zoptymalizować, jeśli wskaźnik do elementu był parametrem szablonu: 'szablon struktura PorównajByMember2 {operator bool() (const T & lhs, const T & rhs) {return lhs. * F

+0

@Czarak: jak do czytania, nie jestem pewien. "Sztuczka" to połączenie wskaźników członków z szablonami, myślę, że to kwestia komfortu pisania szablonów i wiedzy o tym, co można z nimi zrobić. Książka Vandevoorde & Josuttis "Szablony C++ - Kompletny przewodnik" powinna być dobra, ale jej nie przeczytałem. Może być na tyle dorosły i drogi, że jest już lepsza opcja. Spójrz na http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list. Zauważ, że jeśli masz C++ 0x, funkcja lambda może pokonać zapisywanie całej klasy dla tego! –

8

Jak wyjaśniono w innych odpowiedzi trzeba pro zobacz funkcję porównania. Jeśli chcesz zachować definicję tej funkcji blisko wywołania sort (np. Jeśli ma to sens tylko dla tego sortowania), możesz go zdefiniować tam z boost::lambda. Użyj funkcji boost::lambda::bind, aby wywołać funkcję składową.

Do np. sortuj według zmiennej członka lub funkcji 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

Oto moje podejście do rozwiązania tego problemu. Rozciąga odpowiedź Steve Jessop poprzez usunięcie wymogu, aby ustawić argumenty szablon jawnie i dodając opcję również użyć functoins i wskaźniki do metod (pobierające)

#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; 
}