是否可以从地图中的键初始化矢量?

时间:2014-08-20 10:42:41

标签: c++ c++11

How to retrieve all keys (or values) from a std::map and put them into a vector?涵盖了从C ++ 11之前的地图中的键填充std :: vector的方法。

有没有办法在C ++ 11中使用lambdas等执行此操作,这意味着我们可以在一行中执行此操作,以便我们可以从地图初始化向量而不是创建向量并在2个操作中填充它?

e.g。 vector<int> v(???(m.begin(),m.end()));

纯C ++ 11是首选,但提升是可以接受的...目的是在一行中完成此操作而不会过于复杂并且#34;炫耀&#34;,所以它不会混淆给其他开发者。

为了比较&#34;显而易见的&#34; C ++ 11解决方案是:

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

7 个答案:

答案 0 :(得分:4)

在Boost.Range中使用boost::adaptor::map_keys

#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;
    }
}

输出:

1
2
3

答案 1 :(得分:4)

你去,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非常强大。

答案 2 :(得分:2)

如下所示:

#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)
        );
}

请注意,与使用vectorvector填充它的向量的解决方案相比,将迭代器传递给push_back的构造函数是初始化resize的最有效方法默认值首先。

答案 3 :(得分:2)

没有,使用任何std::vector构造函数重载,在一行中的纯C ++ 11中无法做到这一点,没有例如创建自己的迭代器适配器。

两个行中做的很简单,例如:

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

为此目的创建自己的迭代器适配器也很容易,例如:

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};
}

现在,您可以使用以下命令在一行中创建和填充std::vector

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

Live example

答案 4 :(得分:1)

略微改进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; });

或更可读:

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);

可能更好:

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;
}

然后打电话:

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

如果要使用很多。

答案 5 :(得分:0)

std::vector类有两个相关的构造函数:

  1. vector(std::initializer_list<T>) [C ++ 11]
  2. vector(InputIterator first, InputIterator last) [C ++ 98]
  3. 第一个是新的C ++ 11构造函数,它允许您执行以下操作:

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

    第二个允许你做的事情:

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

    我没有看到使用initializer_list构造函数的方法(因为你没有预先提供这些项目),所以你最好的选择是创建一个{{1适用于key_iterator并返回std::map<T, K>::iterator而不是(*i).first的类。例如:

    (*i)

    这还要求您编写std::vector<int> keys{ key_iterator(m.begin()), key_iterator(m.end()) }; 类,您可以使用Boost迭代器适配器来简化任务。使用2行版本可能更容易。

答案 6 :(得分:0)

Whoopse,我意识到这实际上并没有回答你的问题(必须学会正确阅读)!

我可能会这样做:

#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';
}