3

Consider the following struct

struct Pair 
{
   int a;
   std::string b;
};

I have a std::vector<Pair> pairs but I want a std::vector<std::string> listOfBFromPairs which contains all the values of b from the list of pairs.

I would like to do this in a one-liner.

I have used OpenGL before and I have previously created a struct, specified a starting point

pairs.begin() + offsetof(Pair, b)

and given a stride length (the size of the pair object in memory) and the number of things I want to parse and it has done what I am after here.

Is this possible here too using c++ iterators?

The specific use case I have is I want to make an unordered set of Vulkan extension names but Vulkan returns structs which contain the extension name as a member. I will then use the unordered set to check against extensions my application would like to use.

3
  • Do you care about duplicates? If you don't then this is trivial. If you do then your either need to pick a set instead of a vector or you will need to remove the duplicates afterwards, but then it's no longer a one liner. Commented Aug 15, 2020 at 11:07
  • 2
    If you want an unordered_set then why are you creating a vector? Just create the unordered_set directly. That would be a one liner. Commented Aug 15, 2020 at 11:10
  • The vulkan API returns an array of structs. The glfw API returns an array of strings. They're guaranteed to be unique. I'm using an unordered set because I want O(1) lookup Commented Aug 15, 2020 at 12:05

2 Answers 2

3

I want a std::vector<std::string> listOfBFromPairs which contains all the values of b from the list of pairs.

I would like to do this in a one-liner?

Sure you can. You can use std::transform from <algorithm> header like this

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

std::vector<Pair> pairs;
std::vector<std::string> listOfBFromPairs;
listOfBFromPairs.reserve(pairs.size()); // reserve the memory for unwanted reallocations

std::transform(pairs.begin(), pairs.end(),
   std::back_inserter(listOfBFromPairs), [](const Pair& pair) { return pair.b; }
);

However in case you need listOfBFromPairs with no duplicates using unordered_set, you need to create a std::unordered_set<std::string> listOfBFromPairs; not a vector of strings. In that case, you need to insert the strings to unordered_set using std::inserter.

#include <unordered_set>
#include <algorithm> // std::transform
#include <iterator>  // std::inserter

std::vector<Pair> pairs;
std::unordered_set<std::string> listOfBFromPairs;

std::transform(pairs.begin(), pairs.end(),
   std::inserter(listOfBFromPairs, listOfBFromPairs.end()), [](const auto& pair) { return pair.b; }
);
Sign up to request clarification or add additional context in comments.

Comments

0

As you asked for a one-liner, here's a Boost approach (not very readable IMHO):

#include <boost/range/adaptor/transformed.hpp>
#include <functional>

const std::vector<Pair> src;

const auto listOfBFromPairs = boost::copy_range<std::vector<std::string>>(src | boost::adaptors::transformed(std::mem_fn(&Pair::b)));

If the target container is e.g. a std::unordered_set, change the last line to

const auto listOfBFromPairs = boost::copy_range<std::unordered_set<std::string>>(src | boost::adaptors::transformed(std::mem_fn(&Pair::b)));

(where only the instantiation of copy_range changed). You can achieve something similar with C++20 ranges.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.