代码之家  ›  专栏  ›  技术社区  ›  rmeador

如何获取std::map的一组键

  •  31
  • rmeador  · 技术社区  · 16 年前

    今天早上我在写算法,遇到了一个奇怪的情况。我有两个 std::map S.我想在每个关键点的集合上执行一个集合交集(以找出两个地图共用的关键点)。在将来的某个时候,我想我可能也会想在这里执行设置减法。幸运的是,STL包含这两个操作的函数。问题是,我好像不能 std::set 把钥匙拿出来 STD::地图 . 有什么办法吗?我正在寻找一些简单的东西,就像在Java中:

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

    我的理解是我不能使用 std::set_intersection() 直接对迭代器执行函数,因为映射公开 std::pair 对象而不仅仅是键。另外,我认为地图不能保证订单。我也有兴趣对一对 std::multimap S,如果有什么区别的话。

    编辑 :我一开始忘记提到,由于我不得不使用编译器(msvc++6),在boost中可用的大多数漂亮模板技巧都无法使用。

    9 回复  |  直到 8 年前
        1
  •  7
  •   Community CDub    8 年前

    您可以使用通用的boost::transform_迭代器返回只返回键(而不是值)的迭代器。见 How to retrieve all keys (or values) from a std::map and put them into a vector?

        2
  •  14
  •   MSalters    9 年前

    您基本上想要的是一个副本,因为std::map不将键保存在std::set中。std::copy假定值类型是兼容的,这里不是这样。std::map::value_类型是std::pair。您只想复制对的第一部分,这意味着您需要一个std::transform。现在,因为您将在集合上使用insert_迭代器,所以顺序无关紧要。std::set将在插入时排序,即使映射已经排序。

    [编辑]代码可能更容易。在我的头上,不是编译的。

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

    如果你有SGI的select1st,你不需要boost::bind。

    [编辑] 为C++ 14更新

    std::transform(MyMap.begin(), MyMap.end(),
        std::inserter(MySet, MySet.end()),
        [](auto pair){ return pair.first; });
    
        3
  •  12
  •   zvrba    12 年前

    地图 担保令;这就是为什么它被称为 sorted associative container . 您可以使用set_交集和自定义比较器函数,其中列出了第二个变量 here .

    所以,有点像

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

    应该有技巧。(也可以使用boost::lambda和bind来避免编写临时函数。)

    默认运算符<over pairs比较两个组件。因为您只需要对第一部分(map键)进行等价,所以您需要定义自己的比较运算符来提供这种关系(这就是上面的函数所做的)。

        4
  •  8
  •   Antti Huima    16 年前

    在实践中,

    yourmap::const_iterator mi;
    set<key_type> k;
    for (mi = yourmap.begin(); mi != yourmap.end(); ++mi)
      k.insert(mi->first);
    return k; 
    
        5
  •  4
  •   Marius    15 年前

    最好的非SGI、非BoostSTL算法友好解决方案是像这样扩展map::迭代器:

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

    然后像这样使用它们:

            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));
    
        6
  •  2
  •   rlbond    16 年前

    您可以迭代并将每个键添加到一个集合中。集合和映射都是有序的,而无序的变体则不是。

        7
  •  2
  •   Darius Kucinskas    16 年前

    我为你的问题找到了很好的链接 here

    并为您的问题提供一些代码:

        #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;
        }
    
        8
  •  1
  •   Isac Casapu    9 年前

    您也许可以为只使用boost::adapters::map_键生成键的映射创建迭代器,请参见中的示例 boost::adapters::map_key documentation . 这似乎是在Boost 1.43中引入的,应该是C++ 2003兼容的,但是我不知道VC++ 6具体是什么,这是从C++ 98时代开始的。

        9
  •  1
  •   ivannaz    8 年前

    根据ZVRBA的回答和Dianot的评论进行构建:

    只要把接收集合变成成对的向量而不是地图,就可以解决Dianot所指出的问题。因此,使用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;});
    

    因此,如果不构造中间副本或集合,我们就可以有效地得到两个图的交集。该结构符合GCC5.3。