Is it possible to initialize a vector from the keys on the map?

How to extract all keys (or values) from std :: map and put them in a vector? covers ways to fill std :: vector from keys on a pre-C ++ 11 map.

Is there a way to do this in C ++ 11 using lambdas, etc., this means that we can do it in one line so that we can initialize the vector from the map instead of creating the vector and fill it in 2 steps?

eg. vector<int> v(???(m.begin(),m.end()));

Pure C ++ 11 is preferable, but boosting is acceptable ... the goal is to do it on one line without being too complicated and “showing off”, so it doesn't get confused with other developers.

For comparison, the "obvious" C ++ 11 solution:

 vector<int> v; v.reserve(m.size()); //not always needed for(auto &x : map) v.push_back(x.first) 
+6
source share
7 answers

Use boost::adaptor::map_keys in Boost.Range.

 #include <iostream> #include <vector> #include <map> #include <boost/range/adaptor/map.hpp> int main() { const std::map<int, std::string> m = { {1, "Alice"}, {2, "Bob"}, {3, "Carol"} }; auto key_range = m | boost::adaptors::map_keys; const std::vector<int> v(key_range.begin(), key_range.end()); for (int x : v) { std::cout << x << std::endl; } } 

Output:

 1 2 3 
+4
source

Something like the following:

 #include <vector> #include <map> #include <boost/iterator/transform_iterator.hpp> int main() { std::map<std::string, int> m{{"abc", 1}, {"def", 2}}; auto extractor = [](decltype(m)::value_type const& kv) { return kv.first; }; std::vector<std::string> v( boost::make_transform_iterator(m.begin(), extractor) , boost::make_transform_iterator(m.end(), extractor) ); } 

Note that passing iterators to the vector constructor is the most efficient way to initialize vector compared to solutions that use push_back or resize vector that fills it with default values.

+2
source

There you go, C ++ 11 is one-line :)

 #include <iostream> #include <map> #include <vector> #include <algorithm> #include <iterator> int main(int, char**) { std::map<int, std::string> map { {1, "one"}, {2, "two"}, {3, "three"} }; std::vector<int> keys; // Reserve enough space (constant-time) keys.reserve(map.size()); // Retrieve the keys and store them into the vector std::transform(map.begin(), map.end(), std::back_inserter(keys), [](decltype(map)::value_type const &pair) {return pair.first;} );// ^^^^^^^^^^^^^^^^^^^^^^^^^ Will benefit from C++14 auto lambdas // Display the vector std::copy(keys.begin(), keys.end(), std::ostream_iterator<int>(std::cout, " ")); return 0; } 

std::transform is powerful.

+2
source

No, there is no way to do this in pure C ++ 11 on a single line using any of the overloads of the std::vector constructor, without eg. creating your own iterator adapter.

This is trivial to do in two lines, though, for example:

 std::vector<Key> v; for (const auto& p : m) v.push_back(p.first); 

For this purpose, it would be easy to create your own iterator adapter , for example:

 template <typename InputIt> struct key_it : public InputIt { key_it(InputIt it) : InputIt(it) {} auto operator*() { return (*this)->first; } }; // Helper function for automatic deduction of iterator type. template <typename InputIt> key_it<InputIt> key_adapt(InputIt it) { return {it}; } 

Now you can create and populate your std::vector in one line using:

 std::vector<Key> v{key_adapt(std::begin(m)), key_adapt(std::end(m))}; 

Living example

+2
source

The std::vector has two corresponding constructors:

  • vector(std::initializer_list<T>) [C ++ 11]
  • vector(InputIterator first, InputIterator last) [C ++ 98]

The first is the new C ++ 11 constructor, which allows you to do things like:

 std::vector<int> v{ 1, 2, 3 }; 

The second allows you to do things like:

 std::vector<int> w{ v.rbegin(), v.rend() }; // 3, 2, 1 

I don’t see a way to use the initializer_list constructor (since you don’t have any elements available up), so it’s best to create a key_iterator class that works on std::map<T, K>::iterator and returns (*i).first instead of (*i) . For instance:

 std::vector<int> keys{ key_iterator(m.begin()), key_iterator(m.end()) }; 

It also requires that you key_iterator class, which you can use the Boost iterator adapters to simplify the task. It may be easier to just use the 2-line version.

0
source

Greetings, I understand that in fact he does not answer your question (he must learn to read correctly)!

I would probably say something like this:

 #include <map> #include <vector> #include <iostream> int main() { std::map<int, std::string> m = { {1, "A"}, {2, "B"}, {3, "C"} }; std::vector<int> v; v.reserve(m.size()); for(auto&& i: m) v.emplace_back(i.first); for(auto&& i: v) std::cout << i << '\n'; } 
0
source

A small clarification of the Quentin solution :

 std::vector<int> keys(map.size()); transform(map.begin(), map.end(), keys.begin(), [](std::pair<int, std::string> const &p) { return p.first; }); 

or more readable:

 std::vector<int> keys(map.size()); auto get_key = [](std::pair<int, std::string> const &p) { return p.first; }; transform(map.begin(), map.end(), keys.begin(), get_key); 

probably better to have:

 int get_key(std::pair<int, std::string> const &p) { return p.first; } std::vector<int> get_keys(const std::map<int, std::string> &map) { std::vector<int> keys(map.size()); transform(map.begin(), map.end(), keys.begin(), get_key); return keys; } 

then call:

 std::vector<int> keys = get_keys(map); 

if it will be used in lots.

0
source

Source: https://habr.com/ru/post/974087/


All Articles