Given a list of objects, what is the cleanest way to create a functor object to act as a comparator, such that the comparator respects the ordering of the objects in the list. It is guaranteed that the objects in the list are unique, and the list contains the entire space of possible objects.
For example, suppose we have:
const std::vector<std::string> ordering {"dog", "cat", "mouse", "elephant"};
Now we want a function to act as a comparator, say for a map:
using Comparator = std::function<bool(const std::string&, const std::string&>;
using MyMap = std::map<std::string, int, Comparator>;
I have a solution, but it's not what I'd call pretty:
const auto cmp = [&ordering] (const auto& lhs, const auto& rhs)
{
const std::array<std::reference_wrapper<const std::decay_t<decltype(lhs)>, 2> values {lhs, rhs};
return *std::find_first_of(std::cbegin(ordering), std::cend(ordering),
std::cbegin(values), std::cend(values),
[] (const auto& lhs, const auto& rhs) {
return lhs == rhs.get();
}) == lhs;
};
Is there something a little less verbose?
KISS. Build up your solution from reusable primitives with clear semantics.
order_by
takes a projectionA->B
and returns an ordering onA
using the ordering onB
. It optionally takes an ordering onB
(not used here):index_in
takes a containerc
and returns a function that takes an element, and determines its index inc
:We then compose them:
Each component can be independently tested and validated, and the composition "obviously" works. We can also rewrite
index_in
to use a faster lookup than linear, say a map from key to index, and we'd have two implementations that can unit test against each other.I find
order_by
very often useful.index_in
I've never had to use before.This trick makes constructing the resulting
map
on one line, instead of storingcmp
, practical, as the final description is short and clear.is also really pretty looking.
Here is a second approach.
We can move some of the work outside of the lambda.
Then we can write the lambda from first principles instead of algorithms:
The resulting lambda is a bit less verbose.
If you had ranged based algorithms it might also help.
In both my solutions, all elements not in the list are greater than any element in the list, and are equal to each other.
This is basically the same as R. Sahu's answer. But since I'd already typed it up... The primary thing I'm advocating here is keeping
ordering
internal tocmp
, presuming that you don't need it externally.Live Example
Encapsulating the
ordering
withincmp
makes the scope that a reader has to look at when he changesordering
much smaller. (Personally I wouldn't constructcmp
as an Rvalue, I'd just dump it directly into the constructor for the same reason... Though it is becoming a bit of a redonculous one-liner:Live Example
You can use:
See it working at http://ideone.com/JzTNwt.
Just skip the algorithms and write a for-loop:
It might technically be longer than your solution, but I find it way easier to read.
Alternatively, depending on the size of the ordering, could throw both into a map:
I suggest you make the key a structure type containing the key (
std::string
in this case) and the index in the array.Something like