Search code examples
c++sortingstlc++14string-comparison

How to change the default string comparison?


How to sort a vector of strings in this specific order:

  • modulo value 3 from the word length

  • lexicographical comparison on the strings (first digits, lower, upper)

My code:

#include <iostream>
#include <algorithm>
#include <vector>
#include <tuple>
#include <string>

int main() {
    std::vector<std::string> words = {
        "cat1",
        "cata",
        "cataaaa",
        "cataAaa",
        "catD",
        "dogdog",
        "dog"
    };

    std::sort(words.begin(), words.end(), [&](auto const& lhs, auto const& rhs) {
        return std::make_tuple(lhs.length() % 3, lhs)
             < std::make_tuple(rhs.length() % 3, rhs);
    });

    for (auto& word : words) {
        std::cout << word << "\n";
    }
}

Output:

dog
dogdog
cat1
catD
cata
cataAaa
cataaaa

What I want:

dog
dogdog
cat1
cata
catD
cataaaa
cataAaa

Solution

  • I think you want a string comparator function:

    #include <cctype>
    #include <iostream>
    #include <string>
    #include <vector>
    
    bool
    my_strless(std::string const& lhs, std::string const& rhs) noexcept
    {
      auto const sz1 = lhs.size(), sz2 = rhs.size();
    
      // Length is closer to to 3
      if (sz1 % 3 < sz2 % 3) return true;
      if (sz2 % 3 < sz1 % 3) return false;
    
      // Generally shorter
      if (sz1 < sz2) return true;
      if (sz2 < sz1) return false;
    
      // Lexicographically smaller
      for (std::string::size_type i = 0; i != sz1; ++i) {
        auto const ch1 = std::tolower(static_cast<unsigned char>(lhs[i]));
        auto const ch2 = std::tolower(static_cast<unsigned char>(rhs[i]));
    
        if (ch1 < ch2) return true;
        if (ch2 < ch1) return false;
      }
    
      // Equal
      return false;
    }
    
    int
    main()
    {
      std::vector<std::string> words{ "cat1", "cata",   "cataaaa", "cataAaa",
                                      "catD", "dogdog", "dog" };
    
      std::sort(words.begin(), words.end(), my_strless);
    
      for (auto& word : words) {
        std::cout << word << "\n";
      }
    }