What STL algorithm can determine if exactly one item in a container satisfies a predicate?

I need an STL algorithm that takes a predicate and a collection and returns true if one and only one member of the collection satisfies the predicate, otherwise returns false.

How would I do this using STL algorithms?

E.g., to replace the following with STL algorithm code to express the same return value.

int count = 0;

for( auto itr = c.begin(); itr != c.end(); ++itr ) {
    if ( predicate( *itr ) ) {
      if ( ++count > 1 ) {
        break;
      }
    }
}

return 1 == count;

Solution 1:

Two things come to my mind:

std::count_if and then compare the result to 1.

To avoid traversing the whole container in case eg the first two elements already match the predicate I would use two calls looking for matching elements. Something along the line of

auto it = std::find_if(begin,end,predicate);
if (it == end) return false;
++it;
return std::none_of(it,end,predicate);

Or if you prefer it more compact:

auto it = std::find_if(begin,end,predicate); 
return (it != end) && std::none_of(std::next(it),end,predicate);

Credits goes to Remy Lebeau for compacting, Deduplicator for debracketing and Blastfurnance for realizing that we can also use none_of the std algorithms.

Solution 2:

You can use std::count_if to count and return if it is one.

For example:

#include <iostream>
#include <algorithm> // std::count_if
#include <vector>    // std::vector
#include <ios>       // std::boolalpha

template<class Iterator, class UnaryPredicate>
constexpr bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred)
{
    return std::count_if(begin, end, pred) == 1;
}

int main()
{
    std::vector<int> vec{ 2, 4, 3 };
    // true: if only one Odd element present in the container
    std::cout << std::boolalpha
              << is_count_one(vec.cbegin(), vec.cend(),
                  [](const int ele) constexpr noexcept -> bool { return ele & 1; });
    return 0;
}

Update: However, std::count_if counts entire element in the container, which is not good as the algorithm given in the question. The best approach using the standard algorithm collections has been mentioned in @formerlyknownas_463035818 's answer.

That being said, OP's approach is also good as the above mentioned best standard approach, where a short-circuiting happens when count reaches 2. If someone is interested in a non-standard algorithm template function for OP's approach, here is it.

#include <iostream>
#include <vector>    // std::vector
#include <ios>       // std::boolalpha
#include <iterator>  // std::iterator_traits

template<class Iterator, class UnaryPredicate>
bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred)
{
    typename std::iterator_traits<Iterator>::difference_type count{ 0 };
    for (; begin != end; ++begin) {
        if (pred(*begin) && ++count > 1) return false;
    }
    return count == 1;
}

int main()
{
    std::vector<int> vec{ 2, 3, 4, 2 };
    // true: if only one Odd element present in the container
    std::cout << std::boolalpha
              << is_count_one(vec.cbegin(), vec.cend(),
                  [](const int ele) constexpr noexcept -> bool { return ele & 1; });
    return 0;
}

Now that can be generalized, by providing one more parameter, the number of N element(s) has/ have to be found in the container.

template<typename Iterator>
using diff_type = typename std::iterator_traits<Iterator>::difference_type;

template<class Iterator, class UnaryPredicate>
bool has_exactly_n(Iterator begin, const Iterator end, UnaryPredicate pred, diff_type<Iterator> N = 1)
{
    diff_type<Iterator> count{ 0 };
    for (; begin != end; ++begin) {
        if (pred(*begin) && ++count > N) return false;
    }
    return count == N;
}