I have vector of vectors of T:
std::vector<std::vector<T>> vector_of_vectors_of_T;
I want to merge all of them into single vector of T:
std::vector<T> vector_of_T;
I am currently using this method:
size_t total_size{ 0 };
for (auto const& items: vector_of_vectors_of_T){
total_size += items.size();
}
vector_of_T.reserve(total_size);
for (auto const& items: vector_of_vectors_of_T){
vector_of_T.insert(end(vector_of_T), begin(items), end(items));
}
Is there more straightforward method? Like a ready std function? If not, is there more efficient way to do it manually?
It's a nice exercise to try and write up a generic join. The code below takes a nested container R1<R2<T> and returns a joined container R1<T>. Note that because of the allocator parameters in the Standard Library, this is a bit cumbersome. No attempt is being made to check for allocator compatibility etc.
Fortunately, there's action::join function in the upcoming range-v3 library by Eric Niebler, that is quite robust already and works today on Clang:
#include <range/v3/all.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
// quick prototype
template<template<class, class...> class R1, template<class, class...> class R2, class T, class... A1, class... A2>
auto join(R1<R2<T, A2...>, A1...> const& outer)
{
R1<T, A2...> joined;
joined.reserve(std::accumulate(outer.begin(), outer.end(), std::size_t{}, [](auto size, auto const& inner) {
return size + inner.size();
}));
for (auto const& inner : outer)
joined.insert(joined.end(), inner.begin(), inner.end());
return joined;
}
int main()
{
std::vector<std::vector<int>> v = { { 1, 2 }, { 3, 4 } };
// quick prototype
std::vector<int> w = join(v);
std::copy(w.begin(), w.end(), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
// Eric Niebler's range-v3
std::vector<int> u = ranges::action::join(v);
std::copy(u.begin(), u.end(), std::ostream_iterator<int>(std::cout, ",")); std::cout << "\n";
}
Live Example
Using back_inserter and move;
size_t total_size{ 0 };
for (auto const& items: vector_of_vectors_of_T){
total_size += items.size();
}
vector_of_T.reserve(total_size);
for (auto& items: vector_of_vectors_of_T){
std::move(items.begin(), items.end(), std::back_inserter(vector_of_T));
}
Instead of copying, std::move gives it a bit performance enhancement.
I guess you could try std::merge/std::move used in a loop - that are already existing std algorithms. Don't know if that's faster.
Related
i want to initialize a const std::vector<int> member variable in the initializer list of a constructor, given a std::vector<std::tuple<int, float>> constructor argument. The vector should contain all the first tuple items.
Is there a one-liner that extracts an std::vector<int> containing all the first tuple entries from an std::vector<std::tuple<int, float>> ?
With C++20 ranges:
struct X
{
const std::vector<int> v;
template <std::ranges::range R>
requires std::convertible_to<std::ranges::range_value_t<R>, int>
X(R r)
: v{r.begin(), r.end()}
{}
X(const std::vector<std::tuple<int, float>>& vt)
: X{vt | std::ranges::views::elements<0>}
{}
};
With ranges-v3:
struct X
{
const std::vector<int> v;
X(const std::vector<std::tuple<int, float>>& vt)
: v{vt | ranges::views::transform([] (auto t) {
return std::get<0>(t); })
| ranges::to<std::vector>()}
{}
};
And a Frankenstein's monster:
#include <ranges>
#include <range/v3/range/conversion.hpp>
struct X
{
const std::vector<int> v;
X(const std::vector<std::tuple<int, float>>& vt)
: v{vt | std::ranges::views::elements<0>
| ranges::to<std::vector>()}
{}
};
Not a one-liner to setup, but certainly one to use - you can write a function to do the conversion, and then the constructor can call that function when initializing the vector member, eg:
std::vector<int> convertVec(const std::vector<std::tuple<int, float>> &arg)
{
std::vector<int> vec;
vec.reserve(arg.size());
std::transform(arg.begin(), arg.end(), std::back_inserter(vec),
[](const std::tuple<int, float> &t){ return std::get<int>(t); }
);
return vec;
}
struct Test
{
const std::vector<int> vec;
Test(const std::vector<std::tuple<int, float>> &arg)
: vec(convertVec(arg))
{
}
};
Adding to #bolov's answer, let's talk about what you might have liked to do, but can't in a one-liner.
There's the to<>() function from ranges-v3 from #bolov 's answer - it materializes a range into an actual container (a range is lazily-evaluated, and when you create it you don't actually iterate over the elements). There is no reason it shouldn't be in the standard library, if you ask me - maybe they'll add it 2023?
You may be wondering - why do I need that to()? Can't I just initialize a vector by a range? And the answer is - sort of, sometimes, maybe. Consider this program:
#include <vector>
#include <tuple>
#include <ranges>
void foo()
{
using pair_type = std::tuple<int, double>;
std::vector<pair_type> tvec {
{12, 3.4}, {56, 7.8}, { 90, 91.2}
};
auto tv = std::ranges::transform_view(
tvec,
[](const pair_type& p) { return std::get<0>(p);}
);
std::vector<int> vec1 { tv.begin(), tv.end() };
std::vector<std::tuple<int>> vec2 {
std::ranges::transform_view{
tvec,
[](const pair_type& p) { return std::get<0>(p);}
}.begin(),
std::ranges::transform_view{
tvec,
[](const pair_type& p) { return std::get<0>(p);}
}.end()
};
}
The vec1 statement will compile just fine, but the vec2 statement will fail (GodBolt.org). This is surprising (to me anyway)!
You may also be wondering why you even need to go through ranges at all. Why isn't there a...
template <typename Container, typename UnaryOperation>
auto transform(const Container& container, UnaryOperation op);
which constructs the transformed container as its return value? That would allow you to write:
std::vector<int> vec3 {
transform(
tvec,
[](const pair_type& p) { return std::get<0>(p); }
)
}
... and Bob's your uncle. Well, we just don't have functions in the C++ standard library which take containers. It's either iterator pairs, which is the "classic" pre-C++20 standard library, or ranges.
Now, the way I've declared the transform() function, it is actually tricky/impossible to implement generally, since you don't have a way of converting the type of a container to the same type of container but with a different element. So, instead, let's write something a little easier:
template <
typename T,
template <typename> class Container,
typename UnaryOperation
>
auto transform(const Container<T>& container, UnaryOperation op)
{
auto tv = std::ranges::transform_view(container, op);
using op_result = decltype(op(*container.begin()));
return Container<op_result> { tv.begin(), tv.end() };
}
and this works (GodBolt.org).
Yes, there is a one-liner if you use this library in Github.
The code goes like this.
#include <iostream>
#include <tuple>
#include <vector>
#include <LazyExpression/LazyExpression.h>
using std::vector;
using std::tuple;
using std::ref;
using std::cout;
using LazyExpression::Expression;
int main()
{
// Define test data
vector<tuple<int, float>> vecTuple { {1,1.1}, {2,2.2}, {3, 3.3} };
// Initialize int vector with the int part of the tuple (the one-liner :)
vector<int> vecInt = Expression([](const tuple<int, float>& t) { return std::get<int>(t); }, ref(vecTuple))();
// print the results
for (int i : vecInt)
cout << i << ' ';
cout << "\n";
}
// Output: 1 2 3
The header <algorithm> contains a version of std::transform() taking a two input sequences, an output sequence, and a binary function as parameters, e.g.:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main()
{
std::vector<int> v0{1, 2, 3};
std::vector<int> v1{4, 5, 6};
std::vector<int> result;
std::transform(v0.begin(), v0.end(), v1.begin(), std::back_inserter(result),
[](auto a, auto b){ return a + b; });
std::copy(result.begin(), result.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
C++20 introduced range algoirthms which does include std::ranges::views::transform(R, F) and its implementation std::ranges::views::transform_view. I can see how to use this transform() with one range, e.g.:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <ranges>
#include <vector>
int main()
{
std::vector<int> v0{1, 2, 3};
for (auto x: std::ranges::views::transform(v0, [](auto a){ return a + 3; })) {
std::cout << x << ' ';
}
std::cout << '\n';
}
However, there is no version supporting more than one range. So, the question becomes: How to use the range version of transform() with two (or more) ranges? On objective of this approach is to benefit from the lazy evaluation of views and avoid the creation of an intermediate sequence (result in the non-ranges version above). A potential use could look like this (putting the function argument in front to make it easier for a potential solution allowing even more than two ranges):
for (auto v: envisioned::transform([](auto a, auto b){ return a + b; }, v0, v1) {
std::cout << v << ' ';
}
std::cout << '\n';
The way you would like to use transform, where you take an arbitrary number of input ranges is not possible directly with what's available in <algorithm> as of C++20. You can of course write such an algorithm yourself without too much effort.
The example with 2 input ranges can be implemented in C++20 like this:
std::ranges::transform(v0, v1,
std::ostream_iterator<int>(std::cout, " "),
std::plus{});
Here's a demo, and this is specifically the last overload of transform listed here.
There is unfortunately no way to write the equivalent version like this:
for (auto v : std::views::transform(v0, v1, std::plus{})) // no, unfortunately
std::cout << v << " ";
but the above implementation does the same thing. It certainly satisfies your requirements of not having to store the results separately; they can be printed as they're generated.
What you're looking for is the algorithm that range-v3 calls zip_with and what we are proposing in P2214 to add to C++23 under the name zip_transform. There is no such algorithm in C++20.
Until then, the range-v3 version is exactly your use-case:
for (auto v : zip_with([](auto a, auto b){ return a + b; }, v0, v1)) {
std::cout << v << ' ';
}
It can handle an arbitrary number of ranges.
Note that there is no piping version here, just as there is not with regular zip.
The answer blow is how I envisioned to answer the question and I think it still contains some interesting bits on how to actually implement a view. It turns out that P2214 mentioned in #Barry's answer has an interesting view (zip_transform) which does an intermediate step of the solution posted below but actually fully covers the functionality needed to do a multi-range transform!
It seems there are essentially two ingredients to using std::ranges::views::transform() with multiple ranges:
Some way to zip the objects at the corresponding positions of the ranges into a std::tuple, probably retaining the value category of the respective values.
Instead of using an n-ary function to take the elements of the range as parameters the function would rather use a std::tuple and possibly use that to call a corresponding n-ary function.
Using this idea would allow creating a version of transform() dealing with an arbitrary number of ranges, although it is easier to take the function object first rather than extract the last element of a parameter pack:
auto transform(auto&& fun, auto&&... ranges)
{
return std::ranges::views::transform(zip(std::forward<decltype(ranges)>(ranges)...),
[fun = std::forward<decltype(fun)>(fun)]
(auto&& t){ return std::apply(fun, std::forward<decltype(t)>(t)); });
}
The zip view used by this implementation can be implemented in terms of std::tuple:
template <typename... Range>
struct zip_view
: std::ranges::view_base
{
template <typename V>
struct rvalue_view
{
std::shared_ptr<std::decay_t<V>> view;
rvalue_view() = default;
rvalue_view(V v): view(new std::decay_t<V>(std::move(v))) {}
auto begin() const { return this->view->begin(); }
auto end() const { return this->view->end(); }
};
template <typename T>
using element_t = std::conditional_t<
std::is_rvalue_reference_v<T>,
rvalue_view<T>,
T
>;
using storage_t = std::tuple<element_t<Range>...>;
using value_type = std::tuple<std::ranges::range_reference_t<std::remove_reference_t<Range>>...>;
using reference = value_type;
using difference_type = std::common_type_t<std::ranges::range_difference_t<Range>...>;
storage_t ranges;
template <typename> struct base;
template <std::size_t... I>
struct base<std::integer_sequence<std::size_t, I...>>
{
using value_type = zip_view::value_type;
using reference = zip_view::value_type;
using pointer = value_type*;
using difference_type = std::common_type_t<std::ranges::range_difference_t<Range>...>;
using iterator_category = std::common_type_t<std::random_access_iterator_tag,
typename std::iterator_traits<std::ranges::iterator_t<Range>>::iterator_category...>;
using iterators_t = std::tuple<std::ranges::iterator_t<Range>...>;
iterators_t iters;
reference operator*() const { return {*std::get<I>(iters)...}; }
reference operator[](difference_type n) const { return {std::get<I>(iters)[n]...}; }
void increment() { (++std::get<I>(iters), ...); }
void decrement() { (--std::get<I>(iters), ...); }
bool equals(base const& other) const {
return ((std::get<I>(iters) == std::get<I>(other.iters)) || ...);
}
void advance(difference_type n){ ((std::get<I>(iters) += n), ...); }
base(): iters() {}
base(const storage_t& s, auto f): iters(f(std::get<I>(s))...) {}
};
struct iterator
: base<std::make_index_sequence<sizeof...(Range)>>
{
using base<std::make_index_sequence<sizeof...(Range)>>::base;
iterator& operator++() { this->increment(); return *this; }
iterator operator++(int) { auto rc(*this); operator++(); return rc; }
iterator& operator--() { this->decrement(); return *this; }
iterator operator--(int) { auto rc(*this); operator--(); return rc; }
iterator& operator+= (difference_type n) { this->advance(n); return *this; }
iterator& operator-= (difference_type n) { this->advance(-n); return *this; }
bool operator== (iterator const& other) const { return this->equals(other); }
auto operator<=> (iterator const& other) const {
return std::get<0>(this->iters) <=> std::get<0>(other.iters);
}
friend iterator operator+ (iterator it, difference_type n) { return it += n; }
friend iterator operator+ (difference_type n, iterator it) { return it += n; }
friend iterator operator- (iterator it, difference_type n) { return it -= n; }
friend difference_type operator- (iterator it0, iterator it1) {
return std::get<0>(it0.iters) - std::get<0>(it1.iters);
}
};
zip_view(): ranges() {}
template <typename... R>
zip_view(R&&... ranges): ranges(std::forward<R>(ranges)...) {}
iterator begin() const { return iterator(ranges, [](auto& r){ return std::ranges::begin(r); }); }
iterator end() const { return iterator(ranges, [](auto& r){ return std::ranges::end(r); }); }
};
auto zip(auto&&... ranges)
-> zip_view<decltype(ranges)...>
{
return {std::forward<decltype(ranges)>(ranges)...};
}
This implementation makes some decisions about the value_type and the reference type and how to keep track of the different ranges. Other choices may be more reasonable (P2214 makes slightly different, probably better, choices). The only tricky bit in this implementation is operating on the std::tuples which requires a parameter pack containing indices or a suitable set of algorithms on std::tuples.
With all of that in place a multi-range transform can be used nicely, e.g.:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <memory>
#include <ranges>
#include <utility>
#include <tuple>
#include <type_traits>
#include <vector>
// zip_view, zip, and transform as above
int main()
{
std::vector<int> v0{1, 2, 3};
std::vector<int> v1{4, 5, 6};
std::vector<int> v2{7, 8, 9};
for (auto x: transform([](auto a, auto b, auto c){ return a + b + c; }, v0, v1, v2)) {
std::cout << x << ' ';
}
std::cout << '\n';
}
I'd like to iterate over a container (say a std::vector) but not from the beginning. I am basically trying to replicate boost::make_iterator_range(v.begin() + 1, v.end()).
I came up with this:
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
std::vector<int> v {1, 2, 3};
std::for_each_n(v.begin() + 1, v.size() - 1, [](auto& n)
{
std::cout << n << '\n';
});
}
However this seems like a poor solution. Also it requires C++17 while I am looking for a solution that works in C++14.
Is there a better way to achieve this without the use of third party libraries?
std::for_each is in C++11 and with that you can do:
std::for_each(v.begin() + 1, v.end(), [](auto &n) { ... });
To replicate make_iterator_range you need an object with a begin and end function. Something like this.
template <typename T>
struct iterator_range {
iterator_range(T begin, T end) : m_begin(begin), m_end(end) {}
T begin() {
return m_begin;
}
T end() {
return m_end;
}
T m_begin, m_end;
};
Then we can make our make_iterator_range function that return an iterator_range.
#include <vector>
#include <iostream>
template <typename T>
struct iterator_range {
iterator_range(T begin, T end) : m_begin(begin), m_end(end) {}
T begin() {
return m_begin;
}
T end() {
return m_end;
}
T m_begin, m_end;
};
template <typename T>
auto make_iterator_range(T begin, T end) {
return iterator_range<T>(begin, end);
}
int main()
{
std::vector<int> v {1, 2, 3};
auto range = iterator_range<decltype(v.begin())>(v.begin() + 1, v.end());
for (auto& i : range) {
std::cout << i << '\n';
}
for (auto& i : make_iterator_range(v.begin() + 1, v.end())) {
std::cout << i << '\n';
}
}
Is there a better way to achieve this ...
A solution using ranges (courtesy of cigien in comments):
for (int element : v | ranges::drop_view(1))
A solution using span
for (int element : span(&v[1], v.size() - 1))
Of course, there aren't std::ranges nor std::span until C++20, so unless you want to use a third party implementation, you'll need to write your own.
If you don't want to implement your own libraries, nor can use C++20, nor want to use a third party library, then std::for_each an alternative that is in C++11.
Is there a nice implementation of the algorithm to calculate the convolution of two ranges in C++ STL (or even boost)?
i.e. something with prototype (convolution of two ranges a..b and c..d):
template< class Iterator >
void convolution(Iterator a, Iterator b, Iterator c, Iterator d);
which modifies a..b range
Yes std::transform
std::transform(a, b, c, a, Op);
// a b is the the first input range
// c is the start of the second range (which must be at least as large as (b-a)
//
// We then use a as the output iterator as well.
// Op is a BinaryFunction
To answer the comment on how to perform accumulation of state in the comments:
struct Operator
{
State& state;
Operator(Sate& state) : state(state) {}
Type operator()(TypeR1 const& r1Value, TypeR2 const& r2Value) const
{
Plop(state, r1Value, r2Value);
return Convolute(state, r2Value, r2Value);
}
};
State theState = 0;
Operator Op(theState);
I'm not quite sure what a "convolution" from two sequences to one of these two sequences is supposed to be: It seems to be a different understanding than my understanding. Below is a version of convolution using a variable number of iterators. Because I'm actually just too lazy for now, I'll use a somewhat uncommon notion of passing the destination iterator as first argument rather than as last argument. Here is an implementation of a corresponding zip() algorithms:
#include <tuple>
namespace algo
{
template <typename... T>
void dummy(T...)
{
}
template <typename To, typename InIt, typename... It>
To zip(To to, InIt it, InIt end, It... its)
{
for (; it != end; ++it, ++to) {
*to = std::make_tuple(*it, *its...);
algo::dummy(++its...);
}
return to;
}
}
Below is a simple test program I used to verify that the above does what I intended it to do:
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <vector>
enum class e { a = 'a', b = 'b', c = 'c' };
std::ostream& operator<< (std::ostream& out,
std::tuple<int, double, e> const& v)
{
return out << "["
<< std::get<0>(v) << ", "
<< std::get<1>(v) << ", "
<< char(std::get<2>(v)) << "]";
}
int main()
{
typedef std::tuple<int, double, e> tuple;
std::vector<int> v{ 1, 2, 3 };
std::deque<double> d{ 1.1, 2.2, 3.3 };
std::list<e> l{ e::a, e::b, e::c };
std::vector<tuple> r;
algo::zip(std::back_inserter(r), v.begin(), v.end(), d.begin(), l.begin());
std::copy(r.begin(), r.end(),
std::ostream_iterator<tuple>(std::cout, "\n"));
}
This question already has answers here:
How to retrieve all keys (or values) from a std::map and put them into a vector?
(24 answers)
Closed 1 year ago.
Working my way through Effective STL at the moment. Item 5 suggests that it's usually preferable to use range member functions to their single element counterparts. I currently wish to copy all the values in a map (i.e. - I don't need the keys) to a vector.
What is the cleanest way to do this?
You could probably use std::transform for that purpose. I would maybe prefer Neils version though, depending on what is more readable.
Example by xtofl (see comments):
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
template< typename tPair >
struct second_t {
typename tPair::second_type operator()( const tPair& p ) const { return p.second; }
};
template< typename tMap >
second_t< typename tMap::value_type > second( const tMap& m ) { return second_t< typename tMap::value_type >(); }
int main() {
std::map<int,bool> m;
m[0]=true;
m[1]=false;
//...
std::vector<bool> v;
std::transform( m.begin(), m.end(), std::back_inserter( v ), second(m) );
std::transform( m.begin(), m.end(), std::ostream_iterator<bool>( std::cout, ";" ), second(m) );
}
Very generic, remember to give him credit if you find it useful.
You can't easily use a range here because the iterator you get from a map refers to a std::pair, where the iterators you would use to insert into a vector refers to an object of the type stored in the vector, which is (if you are discarding the key) not a pair.
I really don't think it gets much cleaner than the obvious:
#include <map>
#include <vector>
#include <string>
using namespace std;
int main() {
typedef map <string, int> MapType;
MapType m;
vector <int> v;
// populate map somehow
for( MapType::iterator it = m.begin(); it != m.end(); ++it ) {
v.push_back( it->second );
}
}
which I would probably re-write as a template function if I was going to use it more than once. Something like:
template <typename M, typename V>
void MapToVec( const M & m, V & v ) {
for( typename M::const_iterator it = m.begin(); it != m.end(); ++it ) {
v.push_back( it->second );
}
}
With C++11 we have the fancy new for loop:
for (const auto &s : schemas)
names.push_back(s.second);
where schemas is a std::map and names is an std::vector.
This populates the array (names) with values from the map (schemas); change s.second to s.first to get an array of keys.
#include <algorithm> // std::transform
#include <iterator> // std::back_inserter
std::transform(
your_map.begin(),
your_map.end(),
std::back_inserter(your_values_vector),
[](auto &kv){ return kv.second;}
);
Sorry that I didn't add any explanation - I thought that code is so simple that is doesn't require any explanation.
So:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
this function calls unaryOperation on every item from inputIterator range (beginInputRange-endInputRange). The value of operation is stored into outputIterator.
If we want to operate through whole map - we use map.begin() and map.end() as our input range. We want to store our map values into vector - so we have to use back_inserter on our vector: back_inserter(your_values_vector). The back_inserter is special outputIterator that pushes new elements at the end of given (as paremeter) collection.
The last parameter is unaryOperation - it takes only one parameter - inputIterator's value. So we can use lambda:
[](auto &kv) { [...] }, where &kv is just a reference to map item's pair. So if we want to return only values of map's items we can simply return kv.second:
[](auto &kv) { return kv.second; }
I think this explains any doubts.
If you are using the boost libraries, you can use boost::bind to access the second value of the pair as follows:
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
int main()
{
typedef std::map<std::string, int> MapT;
typedef std::vector<int> VecT;
MapT map;
VecT vec;
map["one"] = 1;
map["two"] = 2;
map["three"] = 3;
map["four"] = 4;
map["five"] = 5;
std::transform( map.begin(), map.end(),
std::back_inserter(vec),
boost::bind(&MapT::value_type::second,_1) );
}
This solution is based on a post from Michael Goldshteyn on the boost mailing list.
Using lambdas one can perform the following:
{
std::map<std::string,int> m;
std::vector<int> v;
v.reserve(m.size());
std::for_each(m.begin(),m.end(),
[&v](const std::map<std::string,int>::value_type& p)
{ v.push_back(p.second); });
}
Here is what I would do.
Also I would use a template function to make the construction of select2nd easier.
#include <map>
#include <vector>
#include <algorithm>
#include <memory>
#include <string>
/*
* A class to extract the second part of a pair
*/
template<typename T>
struct select2nd
{
typename T::second_type operator()(T const& value) const
{return value.second;}
};
/*
* A utility template function to make the use of select2nd easy.
* Pass a map and it automatically creates a select2nd that utilizes the
* value type. This works nicely as the template functions can deduce the
* template parameters based on the function parameters.
*/
template<typename T>
select2nd<typename T::value_type> make_select2nd(T const& m)
{
return select2nd<typename T::value_type>();
}
int main()
{
std::map<int,std::string> m;
std::vector<std::string> v;
/*
* Please note: You must use std::back_inserter()
* As transform assumes the second range is as large as the first.
* Alternatively you could pre-populate the vector.
*
* Use make_select2nd() to make the function look nice.
* Alternatively you could use:
* select2nd<std::map<int,std::string>::value_type>()
*/
std::transform(m.begin(),m.end(),
std::back_inserter(v),
make_select2nd(m)
);
}
One way is to use functor:
template <class T1, class T2>
class CopyMapToVec
{
public:
CopyMapToVec(std::vector<T2>& aVec): mVec(aVec){}
bool operator () (const std::pair<T1,T2>& mapVal) const
{
mVec.push_back(mapVal.second);
return true;
}
private:
std::vector<T2>& mVec;
};
int main()
{
std::map<std::string, int> myMap;
myMap["test1"] = 1;
myMap["test2"] = 2;
std::vector<int> myVector;
//reserve the memory for vector
myVector.reserve(myMap.size());
//create the functor
CopyMapToVec<std::string, int> aConverter(myVector);
//call the functor
std::for_each(myMap.begin(), myMap.end(), aConverter);
}
Why not:
template<typename K, typename V>
std::vector<V> MapValuesAsVector(const std::map<K, V>& map)
{
std::vector<V> vec;
vec.reserve(map.size());
std::for_each(std::begin(map), std::end(map),
[&vec] (const std::map<K, V>::value_type& entry)
{
vec.push_back(entry.second);
});
return vec;
}
usage:
auto vec = MapValuesAsVector(anymap);
I thought it should be
std::transform( map.begin(), map.end(),
std::back_inserter(vec),
boost::bind(&MapT::value_type::first,_1) );
We should use the transform function from STL algorithm, the last parameter of transform function could be a function object, function pointer or a lambda function that convert item of map to item of vector. This case map have items have type pair that need to convert to item that has int type for vector. Here is my solution that I use lambda function:
#include <algorithm> // for std::transform
#include <iterator> // for back_inserted
// Map of pair <int, string> need to convert to vector of string
std::map<int, std::string> mapExp = { {1, "first"}, {2, "second"}, {3, "third"}, {4,"fourth"} };
// vector of string to store the value type of map
std::vector<std::string> vValue;
// Convert function
std::transform(mapExp.begin(), mapExp.end(), std::back_inserter(vValue),
[](const std::pair<int, string> &mapItem)
{
return mapItem.second;
});
The other answers mention std::transform, and semantically it's the right choice. But in practice std::accumulate might fit better for this task, because:
it allows adding const to the resulting vector;
it just looks nicer, truly functional-style.
Example (using C++17 syntax):
#include <numeric> // for std::accumulate. Note that it's not in <algorithm> where std::transform is located, thanks to Anton Krug for pointing this out
auto map = std::map<int,bool>{};
map[0]=true;
map[1]=false;
const auto mapValues = std::accumulate(map.begin(), map.end(), std::vector<bool>(map.size()), [](auto& vector, const auto& mapEntry) {
vector.push_back(mapEntry.second);
return vector;
});
Surprised nobody has mentioned the most obvious solution, use the std::vector constructor.
template<typename K, typename V>
std::vector<std::pair<K,V>> mapToVector(const std::unordered_map<K,V> &map)
{
return std::vector<std::pair<K,V>>(map.begin(), map.end());
}