Wie sortiere ich einen STL-Vektor?

75

Ich würde gerne eine sortieren vector

vector<myClass> object;

Wo myclassenthält viele intVariablen. Wie kann ich meine sortieren vectorauf eine bestimmte Datenvariable myClass.

NativeCoder
quelle

Antworten:

80

Überladen Sie weniger als der Operator und sortieren Sie dann. Dies ist ein Beispiel, das ich aus dem Internet gefunden habe ...

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

Quelle: hier

Gabe
quelle
14
Sie möchten op <() const machen und seinen Parameter als const-Referenz übergeben.
18
@Neil, ich habe das Beispiel gepostet, das ich gefunden habe, weil ich keine Zeit hatte, alles zu tippen, Alter. Die IT war ein solides Beispiel und löste das Problem. Ich bin froh, dass Sie 40 Minuten gebraucht haben, um zu entscheiden, ob Sie es ablehnen möchten. Ich könnte sehen, dass es herabgestimmt wird, wenn ich die Quellseite nicht einbeziehe, aber ich tat es. Es ist nicht so, als hätte ich versucht, es als mein eigenes zu verpfänden.
Gabe
8
@Neil Ich gebe zu, dass es eine Weile her ist, seit ich c ++ verwendet habe, aber ich erinnerte mich an einige allgemeine Ideen mit dieser Frage, deshalb habe ich geantwortet. Ich behaupte nicht, dass es perfekt ist, aber es funktioniert, ich habe es selbst versucht. Ich nahm Ihren Vorschlag an und fügte ihn hinzu. Wenn Sie ein anderes Problem damit haben, sprechen Sie stattdessen so herablassend. So zu handeln ist nicht so, dass es um einen der beiden Typen geht.
Gabe
4
Wenn Sie es versucht haben und es "funktioniert", ist Ihr Compiler kaputt. Und bitte nenn mich nicht "Typ".
2
@gmcalab Ihr Missverständnis dessen, was Neil mit "make op <() const" meinte, zeigt, dass Sie den von Ihnen geposteten Code nicht wirklich verstehen. Nur weil etwas für Sie "funktioniert", heißt das nicht, dass es das Richtige ist.
Tyler McHenry
117
std::sort(object.begin(), object.end(), pred());

Dabei pred()ist ein Funktionsobjekt, das die Reihenfolge für Objekte von definiert myclass. Alternativ können Sie definieren myclass::operator<.

Zum Beispiel können Sie ein Lambda übergeben:

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

Oder wenn Sie mit C ++ 03 nicht weiterkommen, ist der Funktionsobjektansatz ( vdas Element, nach dem Sie sortieren möchten):

struct pred {
    bool operator()(myclass const & a, myclass const & b) const {
        return a.v < b.v;
    }
};
Avakar
quelle
@NativeCoder dafür ist der Operator gedacht - Sie können ihn nach Belieben und nach der gewünschten Variablen definieren. Es heißt Operator Overloading cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html .
Amir Rachum
8
Der Prädikatansatz ist viel besser als der Operatorüberladungsansatz, wenn Sie keine generische Reihenfolge für diese bestimmte Klasse haben, sondern sie nur nach diesem Vektor sortieren möchten.
Matthieu M.
15

Mit einem Zeiger auf ein Mitglied können Sie einen einzelnen Komparator schreiben, der mit jedem Datenelement Ihrer Klasse arbeiten kann:

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

Ausgabe:

sorted by string field, c: x y
sorted by integer field, a: y x
sorted by integer field, b: x y
Steve Jessop
quelle
Hallo Steve, ich habe darüber nachgedacht, das gleiche Problem wie diese Frage ohne große Fortschritte zu lösen! Ihre Lösung sieht für mich sehr gut aus. Ich denke, ich hätte lange gebraucht, um es zu finden, wenn überhaupt! Ich habe Myers 'Effective C ++ & Effective STL und Dewhursts C ++ Common Knowledge gelesen. Ich frage mich, ob Sie noch etwas lesen empfehlen könnten. Kennen Sie Bücher, die Beispiele wie das oben genannte enthalten, oder fehlen allgemeinere Vorschläge, die mir helfen, solche Lösungen zu finden?
Paul Caheny
1
@Czarak: Wenn ich es mir noch einmal ansehe, könnte es wahrscheinlich verbessert werden. Beispielsweise könnte es besser optimiert werden, wenn der Zeiger auf das Mitglied ein Vorlagenparameter ist : template <typename T, typename U, U (T::*F)> struct CompareByMember2 { bool operator()(const T &lhs, const T & rhs) { return lhs.*F < rhs.*F; }};. Ob dies möglich ist, hängt davon ab, ob der Anrufer eine Variable verwendet, nach der das Mitglied sortieren kann, oder ob verschiedene Anrufer unterschiedliche spezifische Mitglieder angeben.
Steve Jessop
@Czarak: Was das Lesen betrifft, bin ich mir nicht sicher. Der "Trick" hier ist die Kombination von Elementzeigern mit Vorlagen. Ich denke, es geht darum, Vorlagen bequem zu schreiben und zu wissen, was mit ihnen gemacht werden kann. Vandevoorde & Josuttis 'Buch "C ++ Templates - The Complete Guide" soll gut sein, aber ich habe es nicht gelesen. Es könnte alt genug und teuer genug sein, dass es mittlerweile eine bessere Option gibt. Schauen Sie sich stackoverflow.com/questions/388242/… an . Und beachten Sie, dass wenn Sie C ++ 0x haben, eine Lambda-Funktion möglicherweise das Ausschreiben der gesamten Klasse dafür übertrifft!
Steve Jessop
Hallo Steve, Danke für die sehr hilfreichen Antworten. Ich habe mich mit Lambda-Funktionen befasst, wie Sie vorgeschlagen haben. Dies hat mich zu einem anderen Problem geführt, von dem ich glaube, dass ich es auch hätte, wenn ich eine Lösung in der Art und Weise finden würde, wie Sie es in Ihrer obigen Antwort vorgeschlagen haben. Wenn Sie Zeit sparen können, könnten Sie vielleicht einen Blick darauf werfen? stackoverflow.com/questions/4268848/…
Paul Caheny
Ich habe herausgefunden, dass, wenn Sie eine Funktion bereitstellen, die eine Instanz des Vorlagenobjekts erstellt, die aufrufende Funktion auf diese "sort (begin (vec), end (vec), by_member (& Test :: c))" reduziert werden kann.
Arne
9

Wie in anderen Antworten erläutert, müssen Sie eine Vergleichsfunktion bereitstellen. Wenn Sie die Definition dieser Funktion in der Nähe des sort Aufrufs halten möchten (z. B. wenn dies nur für diese Art sinnvoll ist), können Sie sie genau dort mit definieren boost::lambda. Verwenden Sie boost::lambda::binddiese Option , um die Member-Funktion aufzurufen.

So z. B. nach Mitgliedsvariable oder Funktion sortieren 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));
Benjamin Bannier
quelle
2

Dies ist mein Ansatz, um dies allgemein zu lösen. Es erweitert die Antwort von Steve Jessop, indem die Anforderung zum expliziten Festlegen von Vorlagenargumenten entfernt und die Option hinzugefügt wird, auch Funktionen und Zeiger auf Methoden (Getter) zu verwenden.

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