c++sortingstable-sort

What does comparator return really mean in C++


I am trying to do stable sort in C++ and my array is like this

{{first = "art zero", second = "let3 "}, {first = "own kit dig", second = "let2 "}, {first = "art can", second = "let1 "}}

sort(let.begin(), let.end(),
[](pair<string,string>& a, pair<string,string>& b) {
    int comp = a.first.compare(b.first);
    if(!comp) {
        return a.second.compare(b.second);
    } else {
        return comp;
    }
});

Why it did not sort lexicographically for the first value? And what does the return value really mean in C++ comparator? Thanks


Solution

  • If you simply choose std::map as your container, the default ordering provides storage in lexical sort order, e.g.

    #include <iostream>
    #include <string>
    #include <map>
    
    int main (void) {
        
        std::map<std::string, std::string> let {{"art zero", "let3 "}, 
                                                {"own kit dig", "let2 "},
                                                {"art can", "let1 "}};
        for (const auto& p : let)
            std::cout << p.first << ", " << p.second << '\n';
    }
    

    Example Use/Output

    $ ./bin/map_sort_lex
    art can, let1
    art zero, let3
    own kit dig, let2
    

    Sorting On Both You Cannot Use map/mutimap

    If you must sort telegraphically on both .first and .second you cannot use std::map/std::multimap. They are limited to sorting on .first for storage. An alternative is a std::vector<std::pair<>> with your pairs of strings.

    You can implement your custom compare by writing a simple bool Compare function as follows:

    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    
    bool lexcmp (const std::pair<std::string, std::string>& lhs, 
                 const std::pair<std::string, std::string>& rhs)
    {
        if (lhs.first != rhs.first)
            return lhs.first < rhs.first;
        
        return lhs.second < rhs.second;
    }
    
    int main (void) {
        
        std::vector<std::pair<std::string, std::string>> let {{"art zero", "let3 "}, 
                                                              {"art zero", "let2 "},
                                                              {"own kit dig", "let2 "},
                                                              {"art can", "let1 "}};
        
        std::sort (let.begin(), let.end(), lexcmp);
        
        for (const auto& p : let)
            std::cout << p.first << ", " << p.second << '\n';
    }
    

    Example Use/Output

    After adding an additional "art zero", "let2 " pair to force the sort on .second, you would have:

    $ ./bin/vector_sort_lex
    art can, let1
    art zero, let2
    art zero, let3
    own kit dig, let2
    

    If you can get away with only sorting on .first, then std::map (or std::multimap) handle the sorting for you. To sort on both .first and .second, then the solution above isn't bad either.