Is there a function which operates like std::unique
, but that takes a custom comparison predicate, and retains the last element in an equivalent sequence, instead of the first? The answer is yes if C++14 or C++17 is an option; however I am using C++11.
I am starting with a deque of large heavy objects, sorted by one lightweight field. Some objects have equal values for the lightweight field, and this is unacceptable. I need to discard all but the last object in any sequence with a matching light field.
At present my code calls equal_range
using a custom binary predicate and a helper heavy object, then rewinds the end iterator:
deque<Heavy> heavyDeque(...);
Light helperLight(...);
Heavy helperHeavy(helperLight, );
typedef deque<Heavy>:: iterator HevIt;
pair<HevIt, HevIt> deleteRange = equal_range(
heavyDeque.begin(), heavyDeque.end(), helperHeavy,
[](const Heavy & lhs, const Heavy & rhs) {
return lhs.getLight() < rhs.getLight()})
//I have prior knowledge of at least one match
assert(deleteRange.first != deleteRange.second);
// back up; don't delete the last one
--(deleteRange.second);
heavyDeque.erase(deleteRange.first, deleteRange.second);
I am not too happy with having to pass in helperHeavy
, when all I need from inside it is helperLight
. I would rather my code look like this:
pair<HevIt, HevIt> deleteRange = magical_range_search(
heavyDeque.begin(), heavyDeque.end(),
[helperLight](const Heavy & heavy) {
return helperLight == heavy.getLight()})
Note that my imaginary magical function takes a unary predicate, not a binary one.
something like this, perhaps?
template<typename BidirectionalIterator, typename UnaryPredicate>
pair<BidirectionalIterator,BidirectionalIterator>
magical_range_search(BidirectionalIterator begin,
BidirectionalIterator end, UnaryPredicate p)
{
return {find_if(begin,end,p),
find_if(make_reverse_iterator(end),make_reverse_iterator(begin),p)};
}
But, of course, for the whole thing you can just use std::unique
with a reverse_iterator
, as indicated in the comments:
heavyContainer.erase(heavyContainer.begin(),
unique(make_reverse_iterator(heavyContainer.end()),
make_reverse_iterator(heavyContainer.begin()),
[](Heavy const&lhs, Heavy const&rhs) {
return lhs.getLight() < rhs.getLight();
}).base());
Here reverse_iterator<>::base()
returns the underlying original iterator of the reverse_iterator
returned by unique()
. As unique()
returns the new end of the sequence, the reverse operation returns the new begin of the sequence. The new range is {new_begin, orignal_end}
, and the elements in the range {original_begin, new_begin}
must be erase()
d.