Reputation: 63816
How to retrieve all keys (or values) from a std::map and put them into a vector? covers the ways to populate a std::vector from the keys in a map pre-C++11.
Is there a way to do this in C++11 using lambdas, etc, that means we can do it in one line so we can initialize the vector from the map instead of create a vector and populate it in 2 actions?
e.g. vector<int> v(???(m.begin(),m.end()));
Pure C++11 is preferred but boost is acceptable... the aim is to do this in one line without it being overly complicated and "showing off", so it's not confusing to other developers.
For comparison the "obvious" C++11 solution is:
vector<int> v;
v.reserve(m.size()); //not always needed
for(auto &x : map)
v.push_back(x.first)
Upvotes: 7
Views: 5471
Reputation: 63154
There you go, C++11 one-liner :)
#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's auto lambdas
// Display the vector
std::copy(keys.begin(), keys.end(),
std::ostream_iterator<int>(std::cout, " "));
return 0;
}
std::transform
is freaking powerful.
Upvotes: 4
Reputation: 15524
No, there's no way to do this in pure C++11 in one line using any of std::vector
s constructor overloads, without e.g. creating your own iterator adaptor.
It's trivial to do in two lines though, e.g.:
std::vector<Key> v;
for (const auto& p : m) v.push_back(p.first);
It would also be easy to create your own iterator adaptor for this purpose, 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))};
Upvotes: 2
Reputation: 3589
A slight refinement of Quentin's 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 readably:
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 having:
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 calling:
std::vector<int> keys = get_keys(map);
if it's going to be used lots.
Upvotes: 1
Reputation: 48635
Whoopse, I realize this does not actually answer your question (must learn to read properly)!
I would probably go with 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';
}
Upvotes: 0
Reputation: 3012
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
Upvotes: 5
Reputation: 136495
Something like the following would do:
#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 vector
's constructor is the most efficient way to initialize a vector
compared to solutions that use push_back
or resize
a vector filling it with default values first.
Upvotes: 2
Reputation: 8165
The std::vector
class has two relevant constructors:
vector(std::initializer_list<T>)
[C++11]vector(InputIterator first, InputIterator last)
[C++98]The first is the new C++11 constructor that 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 (as you don't have the items available up-front), so your best bet would be to create a key_iterator
class that works on a std::map<T, K>::iterator
and returns (*i).first
instead of (*i)
. For example:
std::vector<int> keys{ key_iterator(m.begin()), key_iterator(m.end()) };
This also requires you to write the key_iterator
class, which you can use the Boost iterator adapters to simplify the task. It may be easier just to use the 2 line version.
Upvotes: 0