RiaD
RiaD

Reputation: 47619

Convenient way to return filtered collection

Problem

Suppose I have two iterators begin and end of type Iterator and some predicate predicate (stored in obj). I want to implement method some_collection() o that I can write

for(auto element: obj.get_collection()) {
    do_smth()
}

so that it's work only on elements which satisfy predicate (i.e eqiuvalent to smth like this)

for (auto element: range(begin, end)) {
    if (predicate(element)) {
        do_smth();
    }
}

My solution

Approximate implementation I have in mind is the following (pseudocode):

struct Wrapper {
    op++() {
        do {
           ++value;
        while (!predicate(*value));
    }
    op*() {
        return *value;
    }
    op !=(Iterator other) {
        return value != other.value;
    }
    Iterator value;
}

Where begin() of returned object will be like

value = begin;
while (!predicate(*value)) ++value;
return Wrapper(value)

and end() is just Wrapper(end)

Caveats

What I don't like in this implementation:

I could iterate before each dereference (to fix 2nd and 3rd points) but it will make != end check harder (either I need to decrement end in advance or use increment in check itself which means passing input range two times during the cycle)

Requirements

I don't have specific language version requirements and even interested in implementations using not yet approved. But C++11 would be the greatest

I don't have specific requirements for iterator category supported. I believe Mine will work with ForwardIterators.

I'm interested in both understandability of the code and its efficiency.

Any solution that is closer to silver bullet? :)

Upvotes: 2

Views: 208

Answers (1)

James Youngman
James Youngman

Reputation: 3733

You could use a BOOST filter_iterator. Here is the example from the linked page:

struct is_positive_number {                                                                         
  bool operator()(int x) { return 0 < x; }                                                          
  };                                                                                                

int main()                                                                                          
{                                                                                                   
  int numbers_[] = { 0, -1, 4, -3, 5, 8, -2 };                                                      
    const int N = sizeof(numbers_)/sizeof(int);                                                     

  typedef int* base_iterator;                                                                       
    base_iterator numbers(numbers_);                                                                

  // Example using filter_iterator                                                                  
    typedef boost::filter_iterator<is_positive_number, base_iterator>                               
        FilterIter;                                                                                 

  is_positive_number predicate;                                                                     
    FilterIter filter_iter_first(predicate, numbers, numbers + N);                                  
      FilterIter filter_iter_last(predicate, numbers + N, numbers + N);                             

  std::copy(filter_iter_first, filter_iter_last, std::ostream_iterator<int>(std::cout, " "));       
    std::cout << std::endl;                                                                         

  // Example using make_filter_iterator()                                                           
    std::copy(boost::make_filter_iterator<is_positive_number>(numbers, numbers + N),                
                boost::make_filter_iterator<is_positive_number>(numbers + N, numbers + N),          
                        std::ostream_iterator<int>(std::cout, " "));                                
                          std::cout << std::endl;                                                   

  // Another example using make_filter_iterator()                                                   
    std::copy(                                                                                      
          boost::make_filter_iterator(                                                              
                std::bind2nd(std::greater<int>(), -2)                                               
                        , numbers, numbers + N)                                                     

    , boost::make_filter_iterator(                                                                  
              std::bind2nd(std::greater<int>(), -2)                                                 
                  , numbers + N, numbers + N)                                                       

    , std::ostream_iterator<int>(std::cout, " ")                                                    
      );                                                                                            

  std::cout << std::endl;                                                                           

  return boost::exit_success;                                                                       
}                                                                                                   

Upvotes: 1

Related Questions