2010-05-03 28 views
56

Vorrei ordinare una vectorCome ordinare un vettore STL?

vector<myClass> object; 

Dove myclass contiene molti int variabili. Come posso ordinare il mio vector su qualsiasi variabile dati specifica di myClass.

risposta

61

Sovraccarico inferiore all'operatore, quindi ordinare. Questo è un esempio che ho trovato fuori dal web ...

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

Fonte: here

+13

Dovrai rendere op <() const e passare il suo parametro come riferimento const. –

+15

@Neil, ho pubblicato l'esempio che ho trovato perché non avevo il tempo di digitare tutto tizio. L'IT è stato un esempio solido e ha risolto il problema. Sono contento che ti ci siano voluti 40 minuti per decidere di non farlo. Potrei vederlo essere down-votato se non includessi il sito di origine, ma l'ho fatto. Non è che abbia provato a darlo in pegno come se fosse mio. – Gabe

+7

@Neil Ammetto che è passato un po 'di tempo da quando ho usato C++, ma mi sono ricordato alcune idee generali con questa domanda, ecco perché ho risposto. Non sto affermando che sia perfetto, ma funziona, l'ho provato io stesso. Ho preso il tuo suggerimento e l'ho aggiunto. Se hai qualche altro problema, parla piuttosto agendo in modo accondiscendente. Agire in quel modo non è così, SO riguarda entrambi i tipi. – Gabe

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

dove, pred() è un oggetto funzione che definisce l'ordine su oggetti di myclass. In alternativa, è possibile definire myclass::operator<.

Ad esempio, è possibile passare un lambda:

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

Oppure, se sei bloccato con C++ 03, l'approccio funzione di oggetto (v è il membro su cui si desidera ordinare):

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

@NativeCoder questo è ciò che l'operatore è per - si può definirlo comunque ti piace e in base a qualsiasi variabile tu voglia. si chiama Overloading degli operatori http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html. –

+8

L'approccio predicato è molto meglio dell'approccio di overloading dell'operatore se non si dispone di un ordinamento generico di questa particolare classe ma si desidera solo ordinarlo per questo vettore. –

14

Un puntatore-a-membro consente di scrivere un singolo confronto, che può funzionare con qualsiasi membro di dati della classe :

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

uscita:

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

Ciao Steve, ho pensato di risolvere lo stesso problema di questa domanda senza molti progressi! La tua soluzione mi sembra molto buona. Penso che mi ci sarebbe voluto molto tempo per inventarlo, se mai! Ho letto Myers 'Effective C++ ed efficace STL e Dewhurst C++ Common Knowledge. Mi chiedo se potresti raccomandare qualche lettura in più, conosci qualche libro che copra esempi come quello sopra in particolare o, in mancanza, qualche suggerimento più generale per aiutarmi a vedere soluzioni del genere? –

+1

@ Czarak: guardandolo di nuovo, probabilmente potrebbe essere migliorato. Ad esempio, potrebbe ottimizzare meglio se il puntatore al membro fosse un parametro del template: 'template struct CompareByMember2 {bool operator() (const T & lhs, const T & rhs) {return lhs. * F

+0

@ Czarak: come per la lettura, non ne sono sicuro. Il "trucco" qui è la combinazione di puntatori membri con i modelli, penso che sia una questione di essere a proprio agio nello scrivere modelli e sapere cosa si può fare con loro. Vandevoorde & Josuttis 'book "C++ Templates - The Complete Guide" dovrebbe essere buono, ma non l'ho letto. Potrebbe essere abbastanza vecchio e abbastanza costoso che ora c'è un'opzione migliore. Guarda http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list. E nota che se hai C++ 0x, una funzione lambda potrebbe battere la scrittura dell'intera classe per questo! –

8

Come spiegato in altre risposte è necessario pro vide una funzione di confronto. Se si desidera mantenere la definizione di tale funzione vicino alla chiamata sort (ad esempio se ha senso solo per questo tipo) è possibile definirla proprio lìcon boost::lambda. Utilizzare boost::lambda::bind per chiamare la funzione membro.

Ad es. ordina per membro variabile o una funzione 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

questo è il mio approccio per risolvere questo genere. Si estende la risposta di Steve Jessop, eliminando la necessità di impostare gli argomenti di template in modo esplicito e aggiungendo l'opzione di utilizzare anche functoins e puntatori ai metodi (getter)

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