This question already has answers here:
Flattening iterator
(5 answers)
Closed 8 years ago.
If I have a range of ranges, is there a way to join them together and iterate over them as if they were a single range?
By range, I mean any kind of container or iterator pair. Basically like boost's ranges. Boost already have a function for joining two ranges together with boost::join, but that function can't join an arbitary number of ranges.
Basically, I'm looking for a way to do what the the many function does in this piece of code:
std::vector<std::vector<int>> n = {{0, 1}, {2, 3}};
for (auto i : many(n))
std::cout << i << '\n';
Which would output 0, 1, 2, 3.
In C#, I had SelectMany which could do this.
Here is an example of a flattening iterator used with range-based for (taken from here and added boost::make_iterator_range to show range version) (still needs work though):
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/range.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
template <typename Iter>
class flattening_iterator :
public boost::iterator_adaptor<
flattening_iterator<Iter>,
Iter,
typename Iter::value_type::iterator::value_type,
boost::forward_traversal_tag,
typename Iter::value_type::iterator::value_type
>
{
private:
using super_t = boost::iterator_adaptor<
flattening_iterator<Iter>,
Iter,
typename Iter::value_type::iterator::value_type,
boost::forward_traversal_tag,
typename Iter::value_type::iterator::value_type
>;
using inner_iterator = typename Iter::value_type::iterator;
public:
flattening_iterator(Iter it)
: super_t(it),
inner_begin(),
inner_end(),
outer_end(it)
{}
flattening_iterator(Iter begin, Iter end)
: super_t(begin),
inner_begin((*begin).begin()),
inner_end((*begin).end()),
outer_end(end)
{}
using value_type = typename Iter::value_type::iterator::value_type;
private:
friend class boost::iterator_core_access;
inner_iterator inner_begin;
inner_iterator inner_end;
Iter outer_end;
void increment()
{
if (this->base_reference() == outer_end)
return; // At the end
++inner_begin;
if (inner_begin == inner_end)
{
++this->base_reference();
inner_begin = (*this->base_reference()).begin();
inner_end = (*this->base_reference()).end();
}
}
value_type dereference() const
{
return *inner_begin;
}
};
template <typename Iter>
auto flat_iter(Iter it) -> flattening_iterator<Iter>
{
return flattening_iterator<Iter>(it);
}
template <typename Iter>
auto flat_iter(Iter begin, Iter end) -> flattening_iterator<Iter>
{
return flattening_iterator<Iter>(begin, end);
}
template <typename Cont>
auto flatten(Cont& c) -> boost::iterator_range<flattening_iterator<decltype(std::declval<Cont>().begin())>>
{
return boost::make_iterator_range(flat_iter(c.begin(), c.end()), flat_iter(c.end()));
}
int main()
{
std::vector<std::vector<int>> v1{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
for (auto i : flatten(v1))
{
std::cout << i << ' ';
}
}
Output
1 2 3 4 5 6 7 8 9
Related
I am trying to declare in a function a new vector with the value type from an iterator like the following. If this function is not recursive it is compiling but when the function is calling it self does not compile.
template<typename ForwardIterator>
auto Foo(ForwardIterator f, ForwardIterator l) {
typedef typename iterator_traits<ForwardIterator>::value_type T;
auto n = distance(f,l);
vector<T> v(n);
auto h = n /2;
auto m = next(f,h);
auto vr = Foo(f,m); // If this line is commented it is compiling
return v;
}
int main() {
vector<int> v = { 38, 27, 43, 3, 9, 82, 10 };
auto rv = Foo(v.begin(), v.end());
return 0;
}
Your compiler seems to fail to deduce auto type. You could help it to deduce return type by implementing iterator_type2vector_type helper class.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
template<typename ForwardIterator>
struct iterator_type2vector_type {
typedef typename std::iterator_traits<ForwardIterator>::value_type value_type;
typedef std::vector<value_type> vector_type;
};
template<typename ForwardIterator>
typename iterator_type2vector_type<ForwardIterator>::vector_type
Foo(ForwardIterator begin, ForwardIterator end) {
typedef typename iterator_type2vector_type<ForwardIterator>::vector_type vector_type;
// add your code here.
return vector_type(begin, end);
}
int main() {
auto a = {1, 2 ,3, 4};
auto result = Foo(std::begin(a), std::end(a));
std::copy(std::begin(result), std::end(result), std::ostream_iterator<int>(std::cout, ", "));
return 0;
}
auto return type is c++14 feature, so with this changes you would go with c++11.
My class inheriting from boost::iterator_adaptor has code like
private:
using is_bidirectional = std::is_convertible<
iterator_category, std::bidirectional_iterator_tag>;
typename std::enable_if<is_bidirectional::value>::type
decrement() { ... }
and I am wondering if the enable_if part is redundant. As written, the decrement() overload will not be enabled if the adapted iterator is not bidirectional. However I do not know whether boost::iterator_adaptor already uses SFINAE such that I do not need to be concerned about the traversal category in the private overloads. The code compiles and works just fine. I am only asking whether the enable_if part is useful or whether it is redundant.
Yes.
Indeed, the adapter, by default, does mirror the base-iterator's category.
Here's a comprehensive test that works:
Live On Coliru
#include <boost/iterator_adaptors.hpp>
template <typename BaseIterator>
struct Adapt : boost::iterator_adaptor<Adapt<BaseIterator>, BaseIterator> {
using base = boost::iterator_adaptor<Adapt<BaseIterator>, BaseIterator>;
using base::base;
using base::operator=;
};
#include <iostream>
#include <iterator>
#include <typeinfo>
template <typename It>
std::string cat(It const&) {
return typeid(typename std::iterator_traits<It>::iterator_category).name();
}
template <typename It, typename OutIt>
void test(std::string caption, It f, It l, OutIt out) {
Adapt<It> af{f}, al{l};
std::cout << caption << ", adapts to " << cat(af) << "\t";
std::copy(af, al, out);
std::cout << "\n";
}
template <typename Container, typename OutIt>
void test(std::string caption, Container const& c, OutIt out) {
using BaseIterator = typename Container::const_iterator;
Adapt<BaseIterator> f{c.begin()}, l{c.end()};
test(caption, f, l, out);
}
#include <vector>
#include <forward_list>
#include <list>
#include <sstream>
int main() {
using V = std::vector<int>;
using L = std::list<int>;
using FL = std::forward_list<int>;
using II = std::istream_iterator<int>;
using OI = std::ostream_iterator<int>;
static_assert(std::is_same<std::random_access_iterator_tag, std::iterator_traits<V::iterator>::iterator_category>{}, "");
static_assert(std::is_same<std::bidirectional_iterator_tag, std::iterator_traits<L::iterator>::iterator_category>{}, "");
static_assert(std::is_same<std::forward_iterator_tag, std::iterator_traits<FL::iterator>::iterator_category>{}, "");
static_assert(std::is_same<std::input_iterator_tag, std::iterator_traits<II>::iterator_category>{}, "");
static_assert(std::is_same<std::output_iterator_tag, std::iterator_traits<OI>::iterator_category>{}, "");
OI out(std::cout, " ");
test("vector ", V { 1,2,3 }, out);
test("list ", L { 4,5,6 }, out);
test("forward_list", FL { 7,8,9 }, out);
{
std::istringstream iss("10 11 12");
II f(iss), l;
test("stream input", f, l, out);
}
}
Which prints¹
vector , adapts to std::random_access_iterator_tag 1 2 3
list , adapts to std::bidirectional_iterator_tag 4 5 6
forward_list, adapts to std::forward_iterator_tag 7 8 9
stream input, adapts to std::input_iterator_tag 10 11 12
¹ filtered the output through c++filt -t for prettier type info
I wanted to replace the loop with an algorithm in the following code
int numbers[] = { ... };
vector<int> output;
for( int* it = numbers+from; it != numbers+to ; ++it )
{
int square = func( *it );
if( predicate(square) )
{
output.push_back(square);
}
}
The program is meant to transform the values and copy them to a destination if a condition occurs.
I could not use std::copy_if because that would not apply a transformation.
I could not use std::transform because that lacks a predicate
It is not even a good idea to write a transform_copy_if() , because of the intermediate copy of the transformed variable.
It looks like my only hope is to create a conditional_back_insert_iterator. Then I could have a pretty decent call like:
int numbers[] = { ... };
vector<int> output;
std::transform(numbers+from, numbers+to,
conditional_back_inserter(predicate, output),
func);
Is this solution the best way to treat such cases ? I couldn't even google for conditional inserters, so I am worried I'm on the wrong path.
I could also imagine that I could implement an alternative solution such as
std::copy_if( transform_iterator<func>(numbers+from),
transform_iterator<func>(numbers+to),
back_inserter(output) );
(which reminds me of an example of *filter_iterators* in boost)
but that does not offer readability.
I think creating your own iterator is the way to go:
#include <iostream>
#include <vector>
#include <iterator>
#include <functional>
template<class T>
class conditional_back_insert_iterator
: public std::back_insert_iterator<std::vector<T>>
{
private:
using Base = std::back_insert_iterator<std::vector<T>>;
using Container = std::vector<T>;
using value_type = typename Container::value_type;
public:
template<class F>
conditional_back_insert_iterator(Container& other, F&& pred)
: Base(other), c(other), predicate(std::forward<F>(pred))
{ }
conditional_back_insert_iterator<T>& operator*()
{ return *this; }
conditional_back_insert_iterator<T>&
operator=(const value_type& val) const
{
if (predicate(val))
c.push_back(val);
return *this;
}
conditional_back_insert_iterator<T>&
operator=(value_type&& val) const
{
if (predicate(val))
c.push_back(std::move(val));
return *this;
}
private:
Container& c;
std::function<bool (const value_type&)> predicate;
};
template<
class Container,
class F,
class value_type = typename Container::value_type
>
conditional_back_insert_iterator<value_type>
conditional_back_inserter(Container& c, F&& predicate)
{
return conditional_back_insert_iterator<value_type>(c, std::forward<F>(predicate));
}
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int> to;
auto is_even = [] (int x) { return (x % 2) == 0; };
std::copy(v.begin(), v.end(), conditional_back_inserter(to, is_even));
}
Here's my attempt.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template <class Container, class Pred>
class conditional_insert_iterator
: public std::iterator< std::output_iterator_tag, void, void, void, void >
{
public:
explicit conditional_insert_iterator(Container& c, Pred p) : container(&c), pred(p) {}
conditional_insert_iterator& operator=(typename Container::const_reference value) {
if (pred(value))
container->push_back(value);
return *this;
}
conditional_insert_iterator& operator*() {return *this;}
conditional_insert_iterator& operator++() {return *this;}
conditional_insert_iterator& operator++(int) {return *this;}
private:
Container* container;
Pred pred;
};
template< class Container, class Pred>
conditional_insert_iterator<Container, Pred> conditional_inserter( Container& c, Pred pred )
{
return conditional_insert_iterator<Container, Pred>(c, pred);
}
using namespace std;
int main()
{
vector<int> in = { 1, 2, 3, 4, 5, 6 };
vector<int> out;
transform(in.begin(), in.end(),
conditional_inserter(out, [](int i) { return i%2 == 0;}),
[](int i) { return i + 2;});
for (auto i : out)
cout << i << "\n";
return 0;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Flattening iterator
I have a vector of vectors of some type (say int). I would like to iterate
over the collection of elements in the vectors, pretty much like iterating
over a vector of ints.
I can implement something that does this but was wondering how to use boost's
iterator_adapter to do this.
Here is something I hacked up. It works and outputs the correct result, but has a lot of work to be done.
#include <boost/iterator/iterator_adaptor.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
template <typename Iter>
class flattening_iterator :
public boost::iterator_adaptor<
flattening_iterator<Iter>,
Iter,
typename Iter::value_type::iterator::value_type,
boost::forward_traversal_tag,
typename Iter::value_type::iterator::value_type
>
{
private:
using super_t = boost::iterator_adaptor<
flattening_iterator<Iter>,
Iter,
typename Iter::value_type::iterator::value_type,
boost::forward_traversal_tag,
typename Iter::value_type::iterator::value_type
>;
using inner_iterator = typename Iter::value_type::iterator;
public:
flattening_iterator(Iter it)
: super_t(it),
inner_begin(),
inner_end(),
outer_end(it)
{}
flattening_iterator(Iter begin, Iter end)
: super_t(begin),
inner_begin((*begin).begin()),
inner_end((*begin).end()),
outer_end(end)
{}
using value_type = typename Iter::value_type::iterator::value_type;
private:
friend class boost::iterator_core_access;
inner_iterator inner_begin;
inner_iterator inner_end;
Iter outer_end;
void increment()
{
if (this->base_reference() == outer_end)
return; // At the end
++inner_begin;
if (inner_begin == inner_end)
{
++this->base_reference();
inner_begin = (*this->base_reference()).begin();
inner_end = (*this->base_reference()).end();
}
}
value_type dereference() const
{
return *inner_begin;
}
};
template <typename Iter>
auto flatten(Iter it) -> flattening_iterator<Iter>
{
return flattening_iterator<Iter>(it);
}
template <typename Iter>
auto flatten(Iter begin, Iter end) -> flattening_iterator<Iter>
{
return flattening_iterator<Iter>(begin, end);
}
int main()
{
std::vector<std::vector<int>> v1{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
for (auto i = flatten(v1.begin(), v1.end()); i != flatten(v1.end()); ++i)
{
std::cout << *i << ' ';
}
std::cout << std::endl;
std::vector<std::vector<std::string>> v2{{"Hello", "Print"}, {"Me", "Here"}};
std::copy(flatten(v2.begin(), v2.end()), flatten(v2.end()),
std::ostream_iterator<std::string>(std::cout, " "));
}
Using range based for loops in C++0X, I know we'll be able to do :
std::vector<int> numbers = generateNumbers();
for( int k : numbers )
{
processNumber( k );
}
(might be even simpler to write with lambda)
But how should i do if I only want to apply processNumber( k ) to a part of numbers? For example, how should I write this for loop for to apply processNumber() to the half (head or tail) of the numbers? Is "slicing" allowed like in Python or Ruby?
You can use the "sliced" range adaptor from the Boost.Range library:
#include <boost/range/adaptor/sliced.hpp>
using boost::adaptors::sliced;
...
std::vector<int> numbers = generateNumbers();
for( int k : numbers | sliced(0, numbers.size() / 2))
{
processNumber( k );
}
One possibility might be boost's iterator_range
(Not having a compiler which supports range-based for, using BOOST_FOREACH instead. I'd expect range-based for work the same, as long as the container or range has the begin and end method.)
#include <boost/foreach.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
BOOST_FOREACH(int n, boost::make_iterator_range(v.begin(), v.begin() + v.size() / 2)) {
std::cout << n << '\n';
}
}
For convenience you could also make your own slice function, so it would accept indices instead of iterators. Again, it could be based on boost.iterator_range, or not:
#include <cstddef>
#include <iterator>
template <class Iterator>
class iter_pair
{
public:
typedef Iterator iterator;
typedef Iterator const_iterator; //BOOST_FOREACH appears to want this
iter_pair(iterator first, iterator last): first(first), last(last) {}
iterator begin() const { return first; }
iterator end() const { return last; }
private:
iterator first, last;
};
template <class Container>
struct iterator_type
{
typedef typename Container::iterator type;
};
template <class Container>
struct iterator_type<const Container>
{
typedef typename Container::const_iterator type;
};
template <class Container>
iter_pair<typename iterator_type<Container>::type>
slice(Container& c, size_t i_first, size_t i_last)
{
typedef typename iterator_type<Container>::type iterator;
iterator first = c.begin();
std::advance(first, i_first);
iterator last = first;
std::advance(last, i_last - i_first);
return iter_pair<iterator>(first, last);
}
template <class Container>
iter_pair<typename iterator_type<Container>::type>
slice(Container& c, size_t i_last)
{
return slice(c, 0, i_last);
}
//could probably also be overloaded for arrays
#include <cctype>
#include <string>
#include <boost/foreach.hpp>
#include <iostream>
int main()
{
std::string s("Hello world, la-la-la!");
BOOST_FOREACH( char& c, slice(s, 2, 11)) {
if (c == 'l')
c = std::toupper(c);
}
const std::string& r = s;
BOOST_FOREACH( char c, slice(r, r.size() - 1) ) {
std::cout << c << " ";
}
std::cout << '\n';
}
Generally one would probably be working with iterators in the first place, so it might not be that useful.
Something like this may work (unchecked as I don't have access to a C++0x compiler),
Edit: Checked it on VS10, of course I had to fix numurous errors....
Define a class which is a proxy to any container and whose iterators only return a subset of the container. The example I supply is the simplest one giving the first half but it can be made much more general.
template <class Container>
class head_t {
Container& c_;
public:
template <class T>
class iter {
T curr_;
const T& end_;
int limit_; // count how many items iterated
public:
iter(T curr, const T& end)
: curr_(curr)
, end_(end)
, limit_(std::distance(curr_, end_)/2)
{ }
typename Container::value_type operator*() { return *curr_; }
// Do the equivilant for for operator++(int)
iter& operator++() {
if (--limit_ == 0) // finished our slice
curr_ = end_;
else
++curr_;
return *this;
}
bool operator!=(const iter& i) const {
return curr_ != i.curr_;
}
};
head_t(Container& c) : c_(c) {}
iter<typename Container::iterator> begin() {
return iter<typename Container::iterator>(c_.begin(), c_.end());
}
iter<typename Container::iterator> end() {
return iter<typename Container::iterator>(c_.end(), c_.end());
}
};
template <class T>
head_t<T> head(T& t) { return head_t<T>(t); }
And then you use it in the loop:
for( int k : head(numbers) )