Avraam Mavridis
Avraam Mavridis

Reputation: 8920

Iterator over a vector of pairs and use std::copy to print the result

I have the following vector:

std::vector<std::pair<int,std::string>> songs;

I want to use std::copy to pass elements from the vector (let's say the int) to ostream, something like the following which ofcourse doesn't work:

std::copy(songs.begin(),songs.end(),std::ostream_iterator<int>(std::cout,""));

I know that I can do it with this way:

for(auto it=songs.begin(); it!=songs.end(); ++it)
    {
      std::cout << (*it).first;
    }

But I am wondering if it possible in C++ to create an iterator that points only to one of the elements of the pair and then use it to iterate and copy the elements to ostream or how can I make the std::copy above work?

Upvotes: 1

Views: 4101

Answers (1)

Praetorian
Praetorian

Reputation: 109119

As chris mentioned in the comments, the easiest solution is to use std::transform

std::transform( songs.begin(), songs.end(), 
                std::ostream_iterator<int>( std::cout, "\n" ),
                []( decltype(v)::value_type const& p ) -> decltype(p.first) {
                    return p.first;
                }
              );

If you want to go the custom iterator route, create a type that mimics ostream_iterator's behavior, and additionally uses std::get to retrieve only the item you care about.

template<std::size_t N>
struct get_tuple_item_iterator
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{   
    get_tuple_item_iterator( std::ostream& os, std::string term )
    : os_(os)
    , term_(std::move(term))
    {}

    template<typename... T>
    get_tuple_item_iterator& operator=( std::tuple<T...> const& elem )
    {
        static_assert(N < sizeof...(T), "N is out of range");
        os_ << std::get<N>( elem ) << term_;
        return *this;
    }

    template<typename T1, typename T2>
    get_tuple_item_iterator& operator=( std::pair<T1, T2> const& elem )
    {
        static_assert((N == 0) || (N == 1), "N must be 0 or 1 for std::pair");
        return operator=( std::tie( elem.first, elem.second ) );
    }

    get_tuple_item_iterator& operator*() { return *this; }
    get_tuple_item_iterator& operator++() { return *this; }
    get_tuple_item_iterator& operator++( int ) { return *this; }

private:
    std::ostream& os_;
    std::string term_;
};

Use it as

std::copy( songs.begin(), songs.end(), 
           get_tuple_item_iterator<0>( std::cout, "\n" ) );

Upvotes: 6

Related Questions