Liste mit maps sortieren



  • Moin,
    s. Thema, also die list enthält mehrere map < string, string> (key, value) und sortiert werden soll nach einem bestimmten key (title) bitte mal einen kleinen Denkanstoß.

    Danke in Vorab und viele Grüße!



  • @_ro_ro Mir ist nicht klar, wie die Sortierung aussehen soll, kannst du ein Beispiel geben?
    std::list hat eine sort Funktion, der du mitgeben kannst, wie sortiert werden soll.



  • @_ro_ro

    Ein erster Schritt sollte immer einen Blick in die C++ Referenz sein:

    https://en.cppreference.com/w/cpp/container/list/sort

    Die Sortierungsfunktion erlaubt die Angabe einer Vergleichsfunktion. Und diese würde ich ausprobieren.



  • @Schlangenmensch

    die Liste ist ein Teil der Konfiguration (BIN) meiner Website. So wird sie erstellt:

    void Folder(Response &r){
        list < map<string,string> > slice;
        for( auto li = r.BIN.begin(); li != r.BIN.end(); li++){
            string url = li->first;
            map <string,string> hunt = r.BIN[url];
            if( hunt["parent"].compare(r.URL) == 0 ){
                hunt["url"] = url;
                slice.push_back(hunt);
            }
        }
        r.LOOPS["children"] = slice; 
    }
    

    Die map (hunt) hat mehrere name/value Paare wie title, descr. Damit wird ein Template gerendert, das Ergebnis ist HTML: http://rolfrost.de/cpp.chtml
    und das soll nach title sortiert werden.

    MFG

    Nur zum Verständnis, das Template

    <dl>
      `loop_children`
        <dt class='file'>  <a href="`url`" class="outlink"> `title` </a>  </dt>
        <dd> `descr` </dd>
      `endloop` 
    </dl>    
    


  • Generell, ohne weiter auf deinen Code einzugehen:
    Verwende std::vector statt std::list. Vector ist eine Art Array mit Größenangabe, das automatisch wachsen kann, während list eine verkettete Liste ist. List habe ich tatsächlich noch nie produktiv benutzt, da vector praktisch immer besser ist. (Ich habe aber nicht weiter auf deinen Code geguckt, da kommt mir mehr komisch vor, u.a. vermute ich, dass BIN auch ne map ist, aber dann könntest du second für den map-Wert nutzen statt über eckige Klammern first nachzugucken. Siehe außerdem https://en.cppreference.com/w/cpp/language/structured_binding um first/second gleich lesbar zu machen)



  • @_ro_ro Erstmal denke ich, dass, wie @wob geschrieben hat, ein Vektor oder direkt eine Map besser geeignet ist.

    Was das sortieren betrifft, wenn ich dich richtig verstanden habe:

     std::sort(
       std::begin(slice), 
       std::end(slice), 
       [](const map<string,string>& a, const map<string, string>& b)
       {
          return a["title"] < b["title"];
       }
    );
    

    bzw, ich würde

    std::sort(
      std::begin(slice), 
      std::end(slice), 
      [](const auto& a, const auto& b)
         {
            return a["title"] < b["title"];
         }
    );
    

    Unabhängig davon, ob slice jetzt eine list oder ein vectorist.

    Ich habe das gerade aber nicht getestet.



  • @Schlangenmensch

    danke Dir,

    C:\var\www\vhosts\rolfrost.de\httpdocs\cgi-bin\fw.cpp: In lambda function:
    C:\var\www\vhosts\rolfrost.de\httpdocs\cgi-bin\fw.cpp:588:31: error: passing 'const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >' as 'this' argument discards qualifiers [-fpermissive]
                   return a["title"] < b["title"];
                                   ^
    In file included from c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\map:61:0,
                     from C:\var\www\vhosts\rolfrost.de\httpdocs\cgi-bin\fw.cpp:9:
    c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_map.h:494:7: note:   in call to 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = std::__cxx11::basic_string<char>; _Tp = std::__cxx11::basic_string<char>; _Compare = std::less<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::__cxx11::basic_string<char>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::__cxx11::basic_string<char>]'
           operator[](key_type&& __k)
           ^~~~~~~~
    
    

    entweder fehlt eine #include <???> oder meine C++ Version 201402 gibt das nicht her.

    Viele Grüße!!



  • @_ro_ro nutze map.at statt map[], weil die eckigen Klammern die map ändern können, passen sie nicht mit const zusammen.



  • Hups, was @wob sagt. Und, std::sort ist im Header <algorithm>



  • @wob

    ich verstehe nur Bahnhof. Also ich habe jetzt eine dedizierte sort-Funktion

    bool title_sort(map<string,string>& a, map<string, string>& b){
        return a["title"] < b["title"];    
    }
    

    Aber wenn ich sie aufrufe sort(slice.begin(), slice.end(), title_sort); gibts jede Menge Fehlermeldungen

    In file included from c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\algorithm:62:0,
                     from c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\regex:38,
                     from C:\var\www\vhosts\rolfrost.de\httpdocs\cgi-bin\fw.cpp:18:
    c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h: In instantiation of 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = std::_List_iterator<std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >; _Compare = __gnu_cxx::__ops::_Iter_comp_iter<bool (*)(std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&, std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)>]':
    c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:4739:18:   required from 'void std::sort(_RAIter, _RAIter, _Compare) [with _RAIter = std::_List_iterator<std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >; _Compare = bool (*)(std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&, std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)]'
    C:\var\www\vhosts\rolfrost.de\httpdocs\cgi-bin\fw.cpp:588:48:   required from here
    c:\mingw6.2\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1966:22: error: no match for 'operator-' (operand types are 'std::_List_iterator<std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' and 'std::_List_iterator<std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >')
         std::__lg(__last - __first) * 2,
                   ~~~~~~~^~~~~~~~~
    


  • @_ro_ro sagte in Liste mit maps sortieren:

    ich verstehe nur Bahnhof.

    Du darfst nicht a["title"] < b["title"] schreiben, sondern musst a.at("title") < b.at("title") schreiben.

    Also ich habe jetzt eine dedizierte sort-Funktion

    Edit: Ach, ich hatte gehofft, du hättest schon auf vector statt list umgestellt.



  • Für eine list<...> funktioniert std::sort nicht (anders als @Schlangenmensch geschrieben hat), denn es verlangt einen RandomAccessIterator (also direkten Indexzugriff), daher hat diese eine eigene sort Funktion.



  • @wob sagte in Liste mit maps sortieren:

    ...
    Verwende std::vector statt std::list.
    ...

    @_ro_ro
    Das solltest du wirklich tun.



  • @DocShoe

    ne, da muss ich ja meine ganze Template-Klasse ändern. Aber wenn list schon eine eigene Sortierfunktion hat, muß es doch möglich sein, nach "title" sortieren zu können. In Perl ist das eine einzige Zeile seufz.

    MFG



  • Oooops, kaum macht mans richtig

    bool title_sort(map<string,string>& a, map<string, string>& b){
        return a["title"] > b["title"];    
    }
    void Folder(Response &r){
        list < map<string,string> > slice;
        for( auto li = r.BIN.begin(); li != r.BIN.end(); li++){
            string url = li->first;
            map <string,string> hunt = r.BIN[url];
            if( hunt["parent"].compare(r.URL) == 0 ){
                hunt["url"] = url;
                slice.push_back(hunt);
            }
        }
        slice.sort(title_sort);
        r.LOOPS["children"] = slice; 
    }
    

    Isses das auch? Wenn ich den Vergleichsoperator rumdrehe. wird auch andersherum sortiert.

    MFG



  • @_ro_ro
    In C++ auch:

    slice.sort( []( const map<string,string>& lhs, const map<string, string>& rhs ) { return lhs.at( "title" ) < rhs.at( "title" ); } );
    

    Edit 1
    Waren wir vorhin nicht sogar schon bei const-correctness?

    Edit 2
    @wob ouch, fixed



  • at ist eine Funktion und braucht runde Klammern. Edit: ist oben schon gefixt.



  • @wob

    Ahh, danke!!! Und danke Euch!

    Viele Grüße 😉

    PS: Daß list eine sort-Methode hat war der entscheidende Hinweis!



  • @_ro_ro sagte in Liste mit maps sortieren:

    ne, da muss ich ja meine ganze Template-Klasse ändern.

    slice wird doch nur lokal benutzt, wieso musst du dann alles ändern?

    @_ro_ro sagte in Liste mit maps sortieren:

    Aber wenn list schon eine eigene Sortierfunktion hat, muß es doch möglich sein, nach "title" sortieren zu können. In Perl ist das eine einzige Zeile seufz.

    Du hast dich nun mal entschieden, das in C++ zu machen. Das Gejammere, dass es in <hier beliebige Programmiersprache einsetzen> einfacher, besser oder schneller geht hilft keinem weiter. Wenn es in anderen Programmiersprachen besser, schneller oder einfacher geht, warum machst du dann C++?



  • @DocShoe sagte in Liste mit maps sortieren:

    slice wird doch nur lokal benutzt

    Ja schon. Aber meine Templating::Loop funktioniert nunmal auch nur mit bestimmten Datentypen 😉

    Im Übrigen hat meine Anmerkung mit Jammern nicht das Geringste zu tun.

    MFG


Anmelden zum Antworten