13

I have a sequence of values that I'd like to pass to a function that takes a (iterator begin, iterator end) pair. However, I only want every second element in the original sequence to be processed.

Is there a nice way using Standard-Lib/Boost to create an iterator facade that will allow me to pass in the original sequence? I figured something simple like this would already be in the boost iterators or range libraries, but I didn't find anything.

Or am I missing another completely obvious way to do this? Of course, I know I always have the option of copying the values to another sequence, but that's not what I want to do.

Edit: I know about filter_iterator, but that filters on values - it doesn't change the way the iteration advances.

ltjax
  • 15,837
  • 3
  • 39
  • 62
  • Ia asked a slightly different question on the same subject: http://stackoverflow.com/questions/3046747/c-stl-selective-iterator, bt I specifically requested no Boost, not very helpful then :( – rubenvb Apr 16 '11 at 10:59

3 Answers3

6

I think you want boost::adaptors::strided

Nathan Phillips
  • 11,899
  • 1
  • 31
  • 24
3
struct TrueOnEven {
 template< typename T >
 bool operator()(const T&) { return mCount++ % 2 == 0; }
 TrueOnEven() : mCount(0) {}
 private:
  int mCount;
};

int main() {
 std::vector< int > tVec, tOtherVec;
 ...
 typedef boost::filter_iterator< TrueOnEven, int > TakeEvenFilterType;

 std::copy( 
  TakeEvenFilterType(tVec.begin(), tVec.end()),
  TakeEvenFilterType(tVec.end(), tVec.end()),
  std::back_inserter(tOtherVec));
}

To be honest, this is anything else than nice and intuitive. I wrote a simple "Enumerator" library including lazy integrated queries to avoid hotchpotch like the above. It allows you to write:

Query::From(tVec.begin(), tVec.end())
.Skip<2>()
.ToStlSequence(std::back_inserter(tOtherVec));

where Skip<2> basically instantiates a generalized "Filter" which skips every N-th (in this case every second) element.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Paul Michalik
  • 4,331
  • 16
  • 18
  • +1 for recognizing that this is just a silly hack. It also makes many assumptions on how the internal algorithm uses the iterator and how `filter_iterator` uses the predicate. For example if `operator()` is evaluated twice for a given iterator, everything goes out of sync. – ltjax Apr 16 '11 at 15:30
  • Well, strictly speaking it is, if the filter_iterator categorizes to anything else than `std::input_iterator` or `boost::SinglePass`. You should probably write a full-fledged `skipping_iterator` which is harder then it might seem... – Paul Michalik Apr 17 '11 at 08:20
2

Here's Boost's filter iterator. It is exactly what you want.

UPDATE: Sorry, read wrongly-ish. Here's a list of all iterator funkiness in Boost:

http://www.boost.org/doc/libs/1_46_1/libs/iterator/doc/#specialized-adaptors

I think a plain iterator_adaptor with an overloaded operator++ that increments the underlying iterator value twice is all you need.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • No, it is not. The filter iterator filters sequences with a predicate on the values - but I'd like to have a predicate on the iterator (if you wanna think of this as filtering at all). Basically, I just want to present `+=2` as `++` to my internal operator – ltjax Apr 16 '11 at 15:28
  • the problem is that applying (in the implementation) operator++ twice can past end the container, without checking whether skipping element was already the end() iterator. Althogh I am having a for loop in mind for(skip_iterator it = v.begin() ; it != v.end() ; ++it){} – alfC Aug 05 '11 at 18:58
  • 2
    alfC: You would need to check if `it != end` inside the `operator++`, before the second call to the unlying iterator's `operator++`. – rubenvb Aug 06 '11 at 09:15
  • @rubenvb, but for that to be the case the iterator will have to know about the `end` internally. Would you have end as part of the iterator or would you pass it in the `next` (`++`) function? In any case, that doesn't make any sense in the context of what an iterator is. – alfC Dec 14 '16 at 22:49