简体   繁体   English

如何获得 std::map 的 std::set 键

[英]how can I get a std::set of keys to a std::map

I was writing an algorithm this morning and I ran into a curious situation.今天早上我正在写一个算法,我遇到了一个奇怪的情况。 I have two std::map s.我有两个std::map I want to perform a set intersection on the sets of the keys of each (to find which keys are common to both maps).我想对每个键的集合执行集合交集(以查找两个映射共有的键)。 At some point in the future, I think it's likely I'll also want to perform set subtraction here as well.在未来的某个时候,我想我很可能也想在这里执行集合减法。 Luckily, the STL includes functions for both of those operations.幸运的是,STL 包含这两种操作的功能。 The problem is, I can't seem to get a std::set of the keys out of a std::map .问题是,我似乎无法从std::map中得到一个std::set键。 Is there any way to do this?有没有办法做到这一点? I'm looking for something that would be this simple, like it is in Java:我正在寻找这样简单的东西,就像在 Java 中一样:

std::set<Foo> keys = myMap.getKeySet();

My understanding is that I can't use the std::set_intersection() function directly on iterators into the maps because the maps expose std::pair objects instead of just keys.我的理解是,我不能直接在映射中的迭代器上使用std::set_intersection()函数,因为映射公开std::pair对象而不仅仅是键。 Also, I don't think the map guarantees order.另外,我认为地图不能保证秩序。 I'm also interested in performing this same operation on a pair of std::multimap s, if that makes any difference.我也有兴趣在一对std::multimap上执行相同的操作,如果这有什么不同的话。

EDIT : I forgot to mention initially that due to the age of the compiler I'm forced to use (MSVC++ 6), most of the nifty template tricks that are available in boost can not be used.编辑:我最初忘了提到,由于我被迫使用的编译器的年龄(MSVC++ 6),大多数在 boost 中可用的漂亮模板技巧都不能使用。

What you basically want is a copy, as std::map doesn't keep the keys in a std::set. 你基本上想要的是一个副本,因为std :: map不会将键保存在std :: set中。 std::copy assumes that the value types are compatible, which isn't the case here. std :: copy假定值类型是兼容的,这不是这里的情况。 The std::map::value_type is a std::pair. std :: map :: value_type是一个std :: pair。 You want to copy only the first part of the pair, which means you need a std::transform. 您只想复制该对的第一部分,这意味着您需要一个std :: transform。 Now, since you will be using an insert_iterator on the set, order doesn't matter. 现在,由于您将在集合上使用insert_iterator,因此顺序无关紧要。 The std::set will sort on insertion, even though the map was already sorted. 即使地图已经排序,std :: set也会对插入进行排序。

[edit] Code might be easier. [编辑]代码可能更容易。 Top of my head, not compiled. 我的头脑,没有编译。

std::transform(MyMap.begin(), MyMap.end(),
    std::inserter(MySet, MySet.end()),
    boost::bind(&std::pair<Key,Value>::first, _1));

If you've got SGI's select1st, you don't need the boost::bind. 如果你有SGI的select1st,你不需要boost :: bind。

[edit] Updated for C++14 [edit]更新了C ++ 14

std::transform(MyMap.begin(), MyMap.end(),
    std::inserter(MySet, MySet.end()),
    [](auto pair){ return pair.first; });

Map does guarantee order; 地图确实保证秩序; that's why it's called a sorted associative container . 这就是为什么它被称为有序关联容器 You can use set_intersection with a custom comparator function, the second variant listed here . 您可以将set_intersection与自定义比较器功能一起使用,这是此处列出的第二个变体。

So, something like 所以,像

bool your_less(const your_map::value_type &v1, const your_map::value_type &v2)
{ return v1.first < v2.first; }

set_intersection(m1.begin(), m1.end(), m2.begin(), m2.end(), your_output_it, your_less);

should do the trick. 应该做的伎俩。 (It is also possible to use boost::lambda and bind to avoid writing a temporary function.) (也可以使用boost :: lambda和bind来避免编写临时函数。)

The default operator< over pairs compares both components. 默认运算符<over pairs比较两个组件。 Since you need equivalence only over the first part of the pair (the map key), you need to define your own comparison operator that provides such relation (which is what the function above does). 由于您只需要在对的第一部分(映射键)上进行等效,因此您需要定义自己的比较运算符来提供这种关系(这是上面的函数所做的)。

You can use the versatile boost::transform_iterator to return an iterator that returns only the keys (and not the values). 您可以使用通用的boost :: transform_iterator返回仅返回键(而不是值)的迭代器。 See How to retrieve all keys (or values) from a std::map and put them into a vector? 请参阅如何从std :: map中检索所有键(或值)并将它们放入向量中?

In practice, 在实践中,

yourmap::const_iterator mi;
set<key_type> k;
for (mi = yourmap.begin(); mi != yourmap.end(); ++mi)
  k.insert(mi->first);
return k; 

The best non-SGI, non-boost STL algorithm-friendly solution is to extend map::iterator like so: 最好的非SGI,非升级STL算法友好的解决方案是扩展map :: iterator,如下所示:

template<typename map_type>
class key_iterator : public map_type::iterator
{
public:
    typedef typename map_type::iterator map_iterator;
    typedef typename map_iterator::value_type::first_type key_type;

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;

    key_type& operator *()
    {
        return map_type::iterator::operator*().first;
    }
};

// helpers to create iterators easier:
template<typename map_type>
key_iterator<map_type> key_begin(map_type& m)
{
    return key_iterator<map_type>(m.begin());
}
template<typename map_type>
key_iterator<map_type> key_end(map_type& m)
{
    return key_iterator<map_type>(m.end());
}

and then use them like so: 然后像这样使用它们:

        map<string,int> test;
        test["one"] = 1;
        test["two"] = 2;

        set<string> keys;

//      // method one
//      key_iterator<map<string,int> > kb(test.begin());
//      key_iterator<map<string,int> > ke(test.end());
//      keys.insert(kb, ke);

//      // method two
//      keys.insert(
//           key_iterator<map<string,int> >(test.begin()),
//           key_iterator<map<string,int> >(test.end()));

        // method three (with helpers)
        keys.insert(key_begin(test), key_end(test));

You can just iterate through and add each key to a set. 您可以迭代并将每个键添加到集合中。 Sets and maps are both ordered, unordered variants are not. 集合和映射都是有序的,无序变量不是。

I found good link for your question here 我在这里找到了你的问题的良好链接

and have some code for your problem: 并为您的问题提供一些代码:

    #include <iostream>
    #include <map>
    #include <set>
    #include <iterator>

    typedef std::map<std::string, int> MyMap;

    // also known as select1st in SGI STL implementation
    template<typename T_PAIR>
    struct GetKey: public std::unary_function<T_PAIR, typename T_PAIR::first_type>
    {
        const typename T_PAIR::first_type& operator()(const T_PAIR& item) const
        {
            return item.first;
        }
    };

    int main(int argc, char** argv)
    {
        MyMap m1,m2;

        m1["a"] = 1;
        m1["b"] = 2;
        m2["c"] = 3;
        m2["b"] = 3;

        std::set<std::string> s;
        std::transform(m1.begin(), m1.end(), std::inserter(s, s.begin()), GetKey<MyMap::value_type>());
        std::transform(m2.begin(), m2.end(), std::inserter(s, s.begin()), GetKey<MyMap::value_type>());
        std::copy(s.begin(), s.end(), std::ostream_iterator<std::string>(std::cout, " "));
        std::cout << std::endl;
        return 0;
    }

You could perhaps create an iterator for a map which only yields the keys using boost::adapters::map_key, see example in the boost::adapters::map_key documentation . 您也许可以为地图创建一个迭代器,它只使用boost :: adapters :: map_key生成键,请参阅boost :: adapters :: map_key文档中的示例。 This appears to have been introduced in Boost 1.43, and is supposed to be C++ 2003 compatible, but I don't know about VC++ 6 specifically, which is from the C++ 98 era. 这似乎是在Boost 1.43中引入的,并且应该与C ++ 2003兼容,但我不知道VC ++ 6是否具体,这是来自C ++ 98时代。

Building up from the answer from zvrba and the comment from dianot: 从zvrba的答案和dianot的评论中建立起来:

Just make the receiving collection be a vector of pairs instead of a map, and the problem pointed by dianot is over. 只需使接收集合成为对的矢量而不是地图,而dianot指出的问题就结束了。 So, using zvrba example: 所以,使用zvrba示例:

std::vector<std::pair<keytype, valtype>> v;

set_intersection(m1.begin(), m1.end(), m2.begin(), m2.end(),
std::back_inserter(v), []( std::pair<keytype, valtype> const & a,
std::pair<keytype, valtype> const & b){return a.first < b.first;});

So without constructing intermediate copies or sets we can get efficiently the intersection of two maps. 因此,如果不构建中间副本或集合,我们可以有效地获得两个映射的交集。 This construction compiles with gcc5.3. 这个结构用gcc5.3编译。

I had the same problem.我有同样的问题。 I solved it by using function templates我通过使用函数模板解决了它

template <class T, class U>
set<T> getSetFromKeys(map<T,U> mapWithKeys){

  set<T> outputSet;

  for (pair<const T, U> keyValuePair : mapWithKeys) {
    outputSet.insert(keyValuePair.first);
  }

  return outputSet; 
}

In this way I had a generic way of extracting keys from maps in my application.通过这种方式,我有一种从应用程序中的映射中提取键的通用方法。

As MSalters says you might want to just create a set of the keys.正如 MSalters 所说,您可能只想创建一组密钥。 This may be good enough for a small map.这对于小地图来说可能已经足够了。

A quick way to do this is:一个快速的方法是:

std::set<Foo> keys;
for (auto &iter : myMap)
{
   keys.insert(iter.first);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM