3

I have a vector with 5 words in it used multiple times, what i want is to achieve this order of words: "a, zawodowe, wyzsze, podstawowe, bez wyksztalcenia" So i wrote this code, and i wonder if its the optimal way of using sort, or maybe one of the if conditions can be skipped

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


int main()
{
   std::vector<std::string> v = {"wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia",
                                  "wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia",
                                  "wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia",
                                  "wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia", "a" };

//return a>b daje:    zawodowe, wyzsze, podstawowe, bez wyksztalcenia, a
    std::sort(v.begin(), v.end(), [](std::string a, std::string b){
        if(a == "zawodowe" && b == "a") return false;
        if(a == "a" && b == "zawodowe") return true;
        if(a == "wyzsze" && b == "a") return false;
        if(a == "podstawowe" && b == "a") return false;
        if(a == "bez wyksztalcenia" && b == "a") return false;
        return a>b;

    });

    for (std::string i: v) {
        std::cout << i << std::endl;
    }
}
black_gay
  • 143
  • 7
  • 1
    is that not just reverse alphabetical order? Just `std::sort(v.begin(), v.end(), std::greater)` would work? – Alan Birtles Mar 09 '20 at 18:05
  • 1
    I want "a" to be first but i made a mistake in the question, sorry – black_gay Mar 09 '20 at 18:08
  • Will the words always be one of those 5? If not, you'll need to explain the sorting rule more precisely. – Toby Speight Mar 09 '20 at 18:09
  • yes, just the words in the initialization list – black_gay Mar 09 '20 at 18:11
  • As long as you define a strict weak ordering, you should be good. If you only care about == "a" then don't look at the contents of other things that aren't "a". See also https://stackoverflow.com/questions/1293231/stl-ordering-strict-weak-ordering – Kenny Ostrom Mar 09 '20 at 18:12
  • 1
    You can also use multiple stl [algorithms](https://en.cppreference.com/w/cpp/algorithm) like `partition + sort` or `sort + rotate`. Though it might be overkill for this. – SacrificerXY Mar 09 '20 at 18:26
  • You should consider using ordered values if your set of strings is closed and only convert it to string in the presentation layer. What you did here is create a total ordering (excluding possible mistakes, it's hard to analyse. For this reason it's also not safe, std::sort can crash if the ordering is not total) on some set of strings, something that integers have by default. – Sopel Mar 15 '20 at 17:30

2 Answers2

9

This should be sufficient:

[](std::string a, std::string b){
        if(b == "a") return false;
        if(a == "a") return true;
        return a>b;
}
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • thanks. And if i wanted the order: zawodowe, a, wyzsze, podstawowe, bez wyksztalcenia is there a simpler way than this? std::sort(v.begin(), v.end(), [](const std::string& a, const std::string& b){ if(a == "a" && b == "bez wyksztalcenia") return true; if(a == "a" && b == "podstawowe") return true; if(a == "a" && b == "wyzsze") return true; return a>b; – black_gay Mar 09 '20 at 19:48
  • yep, see Toby's answer – Alan Birtles Mar 09 '20 at 20:30
7

Alan's answer works when the set of words is almost alphabetical, with one value moved to an end of the sequence. For a more general approach, we need a mapping from the words to something that sorts in the order we want. Small integers are the obvious choice:

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

int main()
{
    std::vector<std::string> v = {"wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia",
                                  "wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia",
                                  "wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia",
                                  "wyzsze", "podstawowe", "zawodowe","podstawowe",
                                  "zawodowe", "bez wyksztalcenia", "zawodowe", "zawodowe", "wyzsze",
                                  "zawodowe", "zawodowe", "wyzsze", "podstawowe", "bez wyksztalcenia", "a" };


    auto const by_weight = [](std::string& a, std::string& b)
                           {
                               static const std::map<std::string, int> weight
                                   = {{"a", 1},
                                      {"zawodowe", 2},
                                      {"wyzsze", 3},
                                      {"podstawowe", 4},
                                      {"bez wyksztalcenia", 5}};
                               return weight.at(a) < weight.at(b);
                           };
    std::sort(v.begin(), v.end(), by_weight);

    for (auto const& i: v) {
        std::cout << i << '\n';
    }
}

Other things I fixed in passing:

  • Don't using namespace std.
  • Do include <iostream> and <string> before using them.
  • Pass strings by const reference, rather than by value.
  • Use const reference when iterating using for.
  • Prefer '\n' to std::endl when there's no need for the implied flush.
Toby Speight
  • 27,591
  • 48
  • 66
  • 103