How do I find the index of the element being passed to the predicate in STL functions?

Viewed 479

Consider the following piece of code:

    std::vector<int> vec(n);
    for (auto& elem : vec) {
        std::cin >> elem;
    }
    auto count = 0;
    while (!isGood(vec)) {
        auto odd_it = std::find_if(vec.begin(), vec.end(), [] (int num) { return ~(num % 2); });
        auto even_it = std::find_if(vec.begin(), vec.end(), [] (int num) { return num % 2; });
        if(odd_it != vec.end() && even_it != vec.end()) {
            std::swap(vec.at(std::distance(vec.begin(), odd_it)), vec.at(std::distance(vec.begin(), even_it)));
            count++;
        }
        else {
            count = -1;
            break;
        }
    }

(I know its not the entire code, but its enough to recreate the problem)

In this code, the line auto odd_it (and also auto even_it) will return me the first odd element in the vector. However, I wanted the std::find_if to return me an iterator pointing to the first odd element which also has an even index (which in this case coincidentally happens to also be the first odd element too.)

So, in a nutshell, the question is basically to find the index of the element currently being "worked upon" by the function (which has been passed to the predicate)

2 Answers

The most clear solution in C++17 and below is simply to write your own version of find_if that passes an index to the predicate. The implementation is straightforward:

template<class Input_it, class Predicate>
Input_it find_if_with_index(Input_it first, Input_it last, Predicate p) {
    for (std::size_t i = 0; first != last; ++i, ++first) {
        if (p(i, *first))
            return first;
    }
    return last;
}

Alternatively to get index, you might create custom iterator to iterate only on odd index.

with range-v3, you might then do:

auto odd_index_even_value = vec | ranges::view::stride(2)
                                | ranges::view::filter([](int e){ return e % 2 == 0; });
auto even_index_odd_value = vec | ranges::view::drop(1) | ranges::view::stride(2)
                                | ranges::view::filter([](int e){ return e % 2 == 1; });

auto it1 = begin(odd_index_even_value);
auto it2 = begin(even_index_odd_value);

int count = 0;
while (it1 != end(odd_index_even_value) && it2 != end(even_index_odd_value)) {
    std::swap(*it1, *it2);
    ++it1;
    ++it2;
    ++count;
}
if (it1 != end(odd_index_even_value) || it2 != end(even_index_odd_value)) {
    count = -1;   
}

Demo

Related