How to obtain index of element from predicate passed to some STL algorithm?
You could combine several iterators form Boost (not really tested, but compiles with GCC 4.6):
#include <algorithm>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/tuple/tuple.hpp>
int main() {
std::vector<bool> mask;
std::vector<int> a, b;
boost::counting_iterator<size_t> count_begin(0), count_end(a.size());
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(count_begin, a.begin()));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(count_end, a.end()));
typedef decltype(zip_end) zip_iterator;
typedef const zip_iterator::value_type& zip_value;
auto pred = [&mask](zip_value val) {
auto index = val.get<0>();
return index < mask.size() ? mask[index] : true;
};
auto filter_begin = boost::make_filter_iterator(pred, zip_begin, zip_end);
auto filter_end = boost::make_filter_iterator(pred, zip_end, zip_end);
std::transform(filter_begin, filter_end, back_inserter(b), [](zip_value val) {
return val.get<1>();
});
}
However, I think an explicit loop is just simpler here.
Here is another more generalized version of the above code, this time even tested :)
It provides implementations for Python-like map
, filter
and enumerate
functions. This one requires GCC 4.7.
#include <utility>
#include <vector>
#include <iterator>
#include <type_traits>
#include <iostream>
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/size.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/counting_range.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
template<typename... ForwardRange>
using zip_range = boost::iterator_range<
boost::zip_iterator<
boost::tuple<
typename boost::range_iterator<
typename std::remove_reference<ForwardRange>::type>::type...>>>;
template<typename... ForwardRange>
zip_range<ForwardRange...>
zip(ForwardRange&&... ranges) {
return boost::make_iterator_range(
boost::make_zip_iterator(
boost::make_tuple(
boost::begin(std::forward<ForwardRange>(ranges))...)),
boost::make_zip_iterator(
boost::make_tuple(
boost::end(std::forward<ForwardRange>(ranges))...)));
}
template<typename ForwardRange, typename Index>
using enumerating_range = zip_range<
boost::iterator_range<boost::counting_iterator<Index>>,
ForwardRange>;
template<typename ForwardRange, typename Index>
enumerating_range<ForwardRange, Index>
enumerate(ForwardRange&& range, Index start) {
return zip(
boost::counting_range(
start,
static_cast<Index>(start + boost::size(range))),
std::forward<ForwardRange>(range));
}
template<typename Predicate, typename ForwardRange>
using filter_range = boost::iterator_range<
boost::filter_iterator<
Predicate,
typename boost::range_iterator<
typename std::remove_reference<ForwardRange>::type>::type>>;
template<typename Predicate, typename ForwardRange>
filter_range<Predicate, ForwardRange>
filter(Predicate pred, ForwardRange&& range) {
return boost::make_iterator_range(
boost::make_filter_iterator(
pred,
boost::begin(std::forward<ForwardRange>(range))),
boost::make_filter_iterator(
pred,
boost::end(std::forward<ForwardRange>(range))));
}
template<typename UnaryOperation, typename ForwardRange>
using map_range = boost::iterator_range<
boost::transform_iterator<
UnaryOperation,
typename boost::range_iterator<
typename std::remove_reference<ForwardRange>::type>::type>>;
template<typename UnaryOperation, typename ForwardRange>
map_range<UnaryOperation, ForwardRange>
map(UnaryOperation operation, ForwardRange&& range) {
return boost::make_iterator_range(
boost::make_transform_iterator(
boost::begin(std::forward<ForwardRange>(range)),
operation),
boost::make_transform_iterator(
boost::end(std::forward<ForwardRange>(range)),
operation));
}
template<typename UnaryOperation, typename Predicate, typename ForwardRange>
using filter_map_range = map_range<
UnaryOperation,
filter_range<Predicate, ForwardRange>>;
template<typename UnaryOperation, typename Predicate, typename ForwardRange>
filter_map_range<UnaryOperation, Predicate, ForwardRange>
filter_map(UnaryOperation operation, Predicate pred, ForwardRange&& range) {
return map(operation, filter(pred, range));
}
int main() {
std::vector<int> a { 10, 11, 12, 13, 14 };
std::vector<bool> mask { false, true, true, false, true };
std::vector<int> b;
auto enumerator = enumerate(a, 0u);
typedef boost::range_value<decltype(enumerator)>::type enum_value;
boost::push_back(
b,
filter_map(
[](const enum_value& val) {
return val.get<1>();
},
[&mask](const enum_value& val) {
auto i = val.get<0>();
return i < mask.size() ? mask[i] : true;
},
enumerator));
boost::copy(b, std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
If you aren't required to use vectors, the solution becomes somewhat boring:
#include <valarray>
#include <algorithm>
#include <iterator>
#include <iostream>
int main() {
using namespace std;
valarray<int> a { 10, 11, 12, 13, 14 };
valarray<bool> mask { false, true, true, false, true };
valarray<int> b = a[mask];
copy(begin(b), end(b), ostream_iterator<int>(cout, " "));
}
Ok, after a bit of investigation I come out with the first example be the easiest way. However, one should not forget to pass value in lambda by (const) reference for not to take address of local copy of a parameter:
copy_if(a.begin(), a.end(), b.begin(),
[&] (const int& x) -> bool { // <-- do not forget reference here
size_t index = &x - &a[0]; // Still ugly... but simple
return mask[index];
});
My answer:
vector<bool> mask ;
vector<int> a, b;
auto it = std::copy_if (a.begin(), a.end(), b.begin(), [&, index = 0] (const int x) mutable -> bool {
return mask[index++]; // increment index
});
This uses a state-full lambda. index is set to zero just once and incremented every time it is used. edit: requires c++14