2

In my hypothetical app, I receive a list of hotels from the server.

struct Hotel 
{
  std::string name;            // e.g. Hilton, Ritz
  int stars;                   // In range [0..5], 0 stands for "unrated"
  int freeRoomCount;           // Number of available rooms [0..N]
  int parkingPlacesAvailable;  // Number of parking places availalble [0..M]
}
std::vector<Hotel> hotels;

All these items are displayed in a simple List View.
I have to provide different types of sorting. The sorting rules are also dictated by the central server.

The sorting rules are as follows:

std::string sortingRules; 
// "s" - sort by stars count
// "f" - sort by free room count
// "p" - sort by parking places available
// The sortingRules string can be a combination of those values. E.g.:
// "ps" - first goes who has the most number of parking places available, 
//        then goes hotels who has more stars
// More combinations available:
//  "s", "sf", "fp", "pf", "spf", "" etc. 
// (16 possible combinations, the empty string means alphabetical sorting)

So the question is: how to interpret this in C++? Enums and bit mask values doesn't work since they do not provide the 'order' control.

I am curious how would the community solve this kind of task? I feel like there is an approach for solving this type of problem, that's why I don't want to go straightforward and write code like:

if (sortingRules[0] == "s") ...

I am using C++11 along with Qt 5.4. No boost.

3
  • Are you going to make struct arrays with your struct variables? Commented Jun 17, 2015 at 17:30
  • @bobtheboy I think no. I just tried to give a generic example. Actually, I have a set of classes that have interface which provides methods like "int stars()", "int freeRoomCount()" and "int parkingPlacesAvailable()". When I populate the UI List View with these classes, I should rely on "std::string" sorting rule. Commented Jun 17, 2015 at 17:34
  • Can you use std::sort? cplusplus.com/reference/algorithm/sort shows how to use a compare function, and your sorting rules string could be used there. Commented Jun 17, 2015 at 17:43

2 Answers 2

3

I'd use a simple parser that reads a character, applies an std::stable_sort with the character's associated comparison predicate and proceeds to the next character.

The stable_sort part is really important. This means that if you sort the items by star count first, then by parking space, the order of star count will remain.

Sign up to request clarification or add additional context in comments.

1 Comment

That's how I would do it, too. All you need is a function that has a switch based on a single character code for how to sort; you may consider doing the first sort unstable as a possible optimization (if you frequently sort by one key, or if there are a lot of repeated keys)
2

You can make a map between their choice and a functor to sort by, for example

using SortFun = bool(*)(Hotel const&, Hotel const&);
std::map<char, SortFun> sorters {
    {'s', [](Hotel const& lhs, Hotel const& rhs){ return lhs.stars < rhs.stars; }},
    {'f', [](Hotel const& lhs, Hotel const& rhs){ return lhs.freeRoomCount < rhs.freeRoomCount; }},
    {'p', [](Hotel const& lhs, Hotel const& rhs){ return lhs.parkingPlacesAvailable < rhs.parkingPlacesAvailable; }}
};

Then you can use it by asking the user to input the sort criteria key, and then you can lookup the correct lambda to sort by using std::stable_sort. For the combinations of sort criteria, e.g. "ps", you can loop over each sort key and std::stable_sort consecutively in reverse order.

int main()
{
    using SortFun = bool(*)(Hotel const&, Hotel const&);
    std::map<char, SortFun> sorters {
        {'s', [](Hotel const& lhs, Hotel const& rhs){ return lhs.stars < rhs.stars; }},
        {'f', [](Hotel const& lhs, Hotel const& rhs){ return lhs.freeRoomCount < rhs.freeRoomCount; }},
        {'p', [](Hotel const& lhs, Hotel const& rhs){ return lhs.parkingPlacesAvailable < rhs.parkingPlacesAvailable; }}
    };

    std::vector<Hotel> hotels {{"foo", 5, 4, 10},
                               {"bar", 3, 8, 20},
                               {"baz", 4, 5, 15},
                               {"fab", 3, 6, 18}};

    std::string choice;
    std::cout << "Pick a sort criteria s, f, p: ";
    std::cin >> choice;

    for (auto rit = choice.rbegin(); rit != choice.rend(); ++rit)
    {
        auto match = sorters.find(*rit);
        if (match != sorters.end())
        {
            std::stable_sort(begin(hotels), end(hotels), match->second);
        }
    }

    for(auto const& hotel : hotels)
    {
        std::cout << "Name: " << hotel.name << " Stars: " << hotel.stars << " Rooms: " << hotel.freeRoomCount << " Parking: " << hotel.parkingPlacesAvailable << std::endl;
    }
}

The output would be (working demo)

Pick a sort criteria s, f, p: sf
Name: fab Stars: 3 Rooms: 6 Parking: 18
Name: bar Stars: 3 Rooms: 8 Parking: 20
Name: baz Stars: 4 Rooms: 5 Parking: 15
Name: foo Stars: 5 Rooms: 4 Parking: 10

To sort from highest to lowest, just switch all the < to > in the lambda functions.

1 Comment

Thanks a lot guys. I like this C++11 solution!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.