Sort mit mehreren Kriterien



  • Hallo

    Ich bin mir sicher das es schon ähnliche Fragen gegeben hat, aber ich habe noch keine direkte Lösung gefunden.

    Ich will einen vektor sortieren. Dazu überlade ich den Operator <

    bool operator< (const Obj v1, const Obj v2)
    {
      if (v1.Name < v2.Name) return true;
      else return false;
    }
    

    Das funktioniert auch prima, ich sortiere mit sort(v.begin(), v.end());
    Ich möchte jetzt aber nicht nur nach Namen, sondern auch nach Jahrgang etc sortieren. Am liebsten würde ich das so haben:

    sort(v.begin(), v.end(), SortbyName)

    Kann mir jemand sagen wie der Operator < aussehen muss wenn ich im Programm nach mehreren Kriterien sortieren will?

    Danke für Hilfe

    Gruss



  • Typhoon schrieb:

    Am liebsten würde ich das so haben:

    sort(v.begin(), v.end(), SortbyName)

    Das ist möglich. std::sort ist überladen, sodass das dritte Argument ein Funktor sein kann (siehe hier). Das heisst, du musst einen eigenen Funktor (eine Klasse) definieren, die den operator() überlädt, sodass er wie eine Funktion behandelt werden kann.

    Übrigens:

    bool operator< (const Obj v1, const Obj v2)
    {
      if (v1.Name < v2.Name) return true;
      else return false;
    }
    

    kann man auch schreiben:

    bool operator< (const Obj& v1, const Obj& v2) // ich würde als Referenz übergeben
    {
      return (v1.Name < v2.Name); // Vereinfachung
    }
    


  • Funktor ist das Stichwort.

    sort(v.begin(), v.end(), SortbyName)

    Das kannst du dann genau so schreiben.



  • kann gelöscht werden 😃



  • Ich hab mir mal die Mühe gemacht, einen Code dafür zu schreiben, um dir Funktoren ein bisschen verständlicher zu machen 😉

    // Deine Klasse
    struct Obj
    {
    	short Age;
    	std::string Name;
    	Obj(short NewAge, const std::string& NewName);
    };
    
    Obj::Obj(short NewAge, const std::string& NewName)
    : Age(NewAge)
    , Name(NewName)
    {
    }
    
    // Funktor für Sortierung nach Name
    struct SortByName
    {
    	bool operator() (const Obj& Left, const Obj& Right);
    };
    
    bool SortByName::operator() (const Obj& Left, const Obj& Right)
    {
    	return (Left.Name < Right.Name);
    }
    
    // Funktor für Sortierung nach Alter
    struct SortByAge
    {
    	bool operator() (const Obj& Left, const Obj& Right);
    };
    
    bool SortByAge::operator() (const Obj& Left, const Obj& Right)
    {
    	return (Left.Age < Right.Age);
    }
    
    // Ausgabe des Vectors
    void OutputVec(const std::vector<Obj>& Vec)
    {
    	for (std::vector<Obj>::const_iterator i = Vec.begin(); i != Vec.end(); ++i)
    	{
    		std::cout << i->Name << " : " << i->Age << std::endl;
    	}
    	std::cout << std::endl;
    }
    
    int main()
    {
    	std::vector<Obj> Vec;
    	Vec.push_back(Obj(45, "A"));
    	Vec.push_back(Obj(102, "D"));
    	Vec.push_back(Obj(37, "C"));
    	Vec.push_back(Obj(19, "B"));
    
    	std::sort(Vec.begin(), Vec.end(), SortByAge());
    	OutputVec(Vec);
    
    	std::sort(Vec.begin(), Vec.end(), SortByName());
    	OutputVec(Vec);
    
    	std::cin.get();
    }
    


  • Hallo

    Danke für die viele Antworten, aber es hängt gerade ein wenig. Ich habe leider noch nicht mit Funktoren gearbeitet.

    Ich benutzte eine normale class.

    Braucht man diesen Konstruktor überhaupt?

    Obj::Obj(short NewAge, const std::string& NewName) 
    : Age(NewAge) 
    , Name(NewName) 
    { 
    }
    

    Gibts da keine einfachere Methode?

    Danke



  • Ach die Lösung war mal wieder einfach...

    bool byname (Obj const v1, Obj const v2)
    {
       return (v1.Name < v2.Name);
    }
    

    Dann sortieren mit sort(vec.begin(), vec.end(), byname)

    Wieso macht ihr das so umständlich? 😕



  • Sobald du Datenmember hast, sollte zumindest ein Default ctor hin.
    Aber für ein Funktor brauchst du nicht unbedingt einen. Ausser du willst gewisse Daten übergeben, um die dann zu vergleichen. (Was öfters der Fall ist).



  • Typhoon schrieb:

    Braucht man diesen Konstruktor überhaupt?

    Obj::Obj(short NewAge, const std::string& NewName) 
    : Age(NewAge) 
    , Name(NewName) 
    { 
    }
    

    Gibts da keine einfachere Methode?

    Ich hab diesen Konstruktor als Argument bei push_back() benötigt. Je nachdem, wie du die Daten im Container abspeicherst, ist er natürlich nicht nötig.

    Typhoon schrieb:

    Ach die Lösung war mal wieder einfach...

    bool byname (Obj const v1, Obj const v2)
    {
       return (v1.Name < v2.Name);
    }
    

    Dann sortieren mit sort(vec.begin(), vec.end(), byname)

    Wieso macht ihr das so umständlich? 😕

    Weil wir dir Funktoren zeigen wollten. Das was du machst, ist ein einfacher Funktionszeiger. Funktoren (=Funktionsobjekte) sind aber flexibler, weil damit z.B. auch Parameter übergeben werden können. Aber du hast Recht, für deine Anwendung geht es auch mit Funktionszeigern. Trotzdem kannst du dir die Funktoren für die Zukunft merken 😉



  • @Typhoon: ich mache es auch immer über Funktoren. Wahrscheinlich einfach Gewohnheitssache.
    EDIT: und oft denkt man dann auch nicht daran dass es auch einfacher ginge, wenn man schonmal gewöhnt ist es mit Funktoren zu machen 🙂 /EDIT



  • Nexus schrieb:

    Funktoren (=Funktionsobjekte) sind aber flexibler, weil damit z.B. auch Parameter übergeben werden können.

    Dafür gibt es std::tr1::bind . Wenn man das zur Verfügung hat, sind Funktionsobjekte in den meisten Fällen überflüssig.

    Kleine Anmerkung, die vielleicht dem Verständnis der Funktionszeiger zuträglich ist: Dass Funktionsnamen implizit in Zeiger umgewandelt werden ist eine Vereinfachung (nicht falsch verstehen, sie ist natürlich standardkonform). Eigentlich „müsste“ es sort(vec.begin(), vec.end(), &byname) heißen.



  • [quote=".filmor]
    Kleine Anmerkung, die vielleicht dem Verständnis der Funktionszeiger zuträglich ist: Dass Funktionsnamen implizit in Zeiger umgewandelt werden ist eine Vereinfachung (nicht falsch verstehen, sie ist natürlich standardkonform). Eigentlich „müsste“ es sort(vec.begin(), vec.end(), &byname) heißen.[/quote]

    Warum gilt das eigentlich für Pointer auf Memberfunktionen nicht?



  • Weil Memberfunktionen einen "versteckten" Parameter haben, und zwar die Adresse des Objektes.

    Also
    void MeineKlasse::meineFunktion(int narf)

    ist eigentlich
    void meineFunktion(MeineKlasse* this, int narf)

    Die Adresser der Funktion reicht in diesem Fall also nicht, da die Adresse des Objektes fehlt.



  • @Fellhuhn: die Super-Spezialbehandlung function ref->function ptr ist ein C-Relikt. Mit T::F funktioniert es auch NICHT wenn die Funktion statisch ist, mit dem this Zeiger hat das also nix zu tun. Ich denke man wollte nicht eine Abschäulichkeit wiederholen die man für freie Funktionszeiger nur in Kauf genommen hat um mit C kompatibel zu bleiben.


Anmelden zum Antworten