5

I'm using GCC 9.2.0 and boost 1.55.

I have 2 vectors:

vector< pair< string, int > > source;
vector< string > dest;

I need to transform the source vector to the dest, such that it should contain only string elements of source vector.

Is it possible using boost::push_back and adaptors?

boost::range::push_back( dest, source | /* adaptor ??? */ );

Currently I have this workable code, but it should be changed:

transform( source.begin(), source.end(), back_inserter(dest), __gnu_cxx::select1st< std::pair< std::string, int > >() );
cigien
  • 57,834
  • 11
  • 73
  • 112
Hardwired
  • 804
  • 1
  • 8
  • 19

4 Answers4

4

To continue the trend of posting mimimal improvements:

Live On Compiler Explorer

#include <boost/range/adaptor/map.hpp>
#include <fmt/ranges.h>

using namespace std::string_literals;
using namespace boost::adaptors;

template <typename R>
auto to_vector(R const& range) {
    return std::vector(boost::begin(range), boost::end(range));
}

int main() {
    std::vector source {std::pair {"one"s,1},{"two",2},{"three",3}};

    fmt::print("keys {}\nvalues {}\n",
            source | map_keys,
            source | map_values);
    
    // if you really need the vectors
    auto keys   = to_vector(source | map_keys);
    auto values = to_vector(source | map_values);
}

Prints

keys {"one", "two", "three"}
values {1, 2, 3}
sehe
  • 374,641
  • 47
  • 450
  • 633
3

You could use std::transform and std::back_inserter with a lambda instead:

#include <algorithm> // transform
#include <iterator>  // back_inserter

std::transform(source.begin(), source.end(), std::back_inserter(dest),
               [](auto& p) {
                   return p.first;
               });
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
3

Here's a boost solution with the transformed adaptor:

#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

using boost::adaptors::transformed;

// define function object (so we can pass it around) emulating std::get
template<std::size_t N>
auto constexpr get = [](auto const& x){ return std::get<N>(x); };

int main()
{
    // prepare sample input
    std::vector<std::pair<std::string,int>> source{{"one",1},{"two",2},{"three",3}};

    // the line you look for
    auto dest = source | transformed(get<0>);

    // dest is a vector on which we can iterate
    for (auto const& i : dest) {
        std::cout << i << '\n';
    }

    // if you really need it to be a vector
    auto dest_vec = boost::copy_range<std::vector<std::string>>(dest);
}
Enlico
  • 23,259
  • 6
  • 48
  • 102
  • This this similar to what i was looking for. Thanks! – Hardwired Jan 19 '21 at 15:29
  • This is nice. I made [a C++20 version](https://godbolt.org/z/aKeorn). Is it possible to get a `std::vector` directly out of the transformation? This `dest` is `const` if I'm not mistaken? – Ted Lyngmo Jan 19 '21 at 15:34
  • 2
    @TedLyngmo, `dest` is a view. And you can get an actual vector via `boost::copy_range`. Please, see my change. – Enlico Jan 19 '21 at 15:36
  • 1
    Nice. `std::vector dest_vec(dest.begin(), dest.end());` seems to work too. You got my vote :-) – Ted Lyngmo Jan 19 '21 at 15:39
  • @TedLyngmo, in that case you are calling directly the overload number 5 of [`std::vector<>::vector`](https://en.cppreference.com/w/cpp/container/vector/vector). `boost::copy_range` seems to do something similar, even though it relies on `boost::begin`/`boost::end`. – Enlico Jan 19 '21 at 15:40
  • @Enlico Yes, that's the one I like. I use it whenever possible. – Ted Lyngmo Jan 19 '21 at 15:41
  • 1
    @TedLyngmo, you got mine too. – Enlico Jan 19 '21 at 15:43
  • 2
    Boost has more convenience than that! https://godbolt.org/z/o5Wh7o (/cc @TedLyngmo opted to [post as another contender](https://stackoverflow.com/a/65796079/85371) – sehe Jan 19 '21 at 17:01
  • 1
    @sehe Briliant! It's not often I've upvoted a question, provided an answer and upvoted _all_ the other answers :-) I learned a lot by you all. – Ted Lyngmo Jan 20 '21 at 07:58
3

Here's a solution using the range-v3 library:

auto dest = source 
          | ranges::views::keys 
          | ranges::to<std::vector<std::string>>;

Here's a demo.


In C++20, there's no std::ranges::to, but you can still copy over the keys to dest if it already exists:

std::ranges::copy(source | std::views::keys, 
                  std::back_inserter(dest));

Here's a demo.

Note that this doesn't work in Clang trunk, which I assume is a bug.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • Yet another nice one - Here's [my C++20 attempt](https://godbolt.org/z/8aK4xP) - I couldn't find the standard version of `ranges::to` though and I also tried using `fmt::print` from `` but it seems neither `g++` nor `clang++` have a `` header yet. – Ted Lyngmo Jan 20 '21 at 07:52
  • 1
    @TedLyngmo Nice :) Yeah, no one has implemented `` yet. I actually wanted to post a solution that used `std::ranges::copy` to fill up `dest`, but couldn't get it to work. I meant to write a question about it, but got sidetracked. Thanks for the comment, it reminded me :) btw, `std::views` is shorthand for `std::ranges::views`, so that's a few characters less out of the box. – cigien Jan 20 '21 at 07:57
  • You're welcome :-) I found why I couldn't find `ranges::to`: [A Plan for C++23 Ranges - P2214r0 - View adjuncts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2214r0.html#view-adjuncts): "_One critical piece of missing functionality is `ranges::to` [[P1206R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2214r0.html#ref-P1206R1)]._" – Ted Lyngmo Jan 20 '21 at 08:20
  • 1
    @TedLyngmo Hmm, it does work in GCC, but not in Clang. I assume it's a Clang bug, so I've updated a solution with C++20 ranges as well. – cigien Jan 20 '21 at 08:21
  • 1
    @TedLyngmo Oh yeah, I was quite disappointed when `to` didn't make it into C++20 :( But we can't have all the goodies at once, I guess :p – cigien Jan 20 '21 at 08:22