6

Let's say we have std::set<int> and we want to create a std::vector<int> with all values from that set:

std::set<int> set;
std::vector<int> vec( set.begin(), set.end() );

This is simple and elegant. But let's say I have a std::map<std::string,int> and I want to copy all values to std::vector<int>. Unfortunately there is no constructor, that accepts range of iterators and converter function. Why there is no such constructor provided? Is there another simple and elegant way to initialize one container with different type values?

4 Answers 4

8

Use transform iterators:

#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <map>

int main() {
    std::map<int, double> m;
    auto f = [](auto&& pair) { return pair.second; };
    std::vector<double>(boost::make_transform_iterator(m.begin(), f),
                        boost::make_transform_iterator(m.end(), f));
}

Alternatively, use boost::adaptors:

#include <boost/range/adaptor/map.hpp>
#include <vector>
#include <map>

int main() {
    std::map<int, double> m;
    auto range = boost::adaptors::values(m);
    std::vector<double>(range.begin(), range.end());
}

Or the same as above:

    auto v = boost::copy_range<std::vector<double> >(boost::adaptors::values(m));

Note that using vector's range constructor is more efficient than a solution involving back_inserter.

Sign up to request clarification or add additional context in comments.

2 Comments

Personally, I prefer the "pipe" operator when using boost adaptors auto range = m | boost::adaptors::values;
@Alan I prefer function call operator for function calls, and it is shorter to type (` | ` vs ()).
2

With boost::transform_iterator:

#include <functional>
#include <boost/iterator/transform_iterator.hpp>

std::map<std::string, int> m{ {"a", 1}, {"b", 2} };
auto second = std::mem_fn(&std::map<std::string, int>::value_type::second);

std::vector<int> vec(boost::make_transform_iterator(std::begin(m), second)
                   , boost::make_transform_iterator(std::end(m), second));

DEMO

3 Comments

Perhaps the downvoter (not me) didn't yet have time to write a comment and is still writing it
Not me, but calling through std::mem_fn would likely not be inlined.
@AaronMcDaid that's gonna be pretty damn long comment
1

std::map has another memory model, storing values as std::pair. Unpacking these isn't the job of a constructor. You can create your vector, reserve memory equal to the map size and iterate over map pairs to push your integers to the back of your vector. std::transform slims that.

2 Comments

std::set also has another memory model, that does not stop me from initailizing a vector from range of it.
Because your set iterators return just the expected interger, a map returns a pair which can't be casted to the expected output.
0

I think the best you can do is using range based cycles or for_each function.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.