【问题标题】:Iterate keys in a C++ map迭代 C++ 映射中的键
【发布时间】:2010-11-29 10:49:12
【问题描述】:

有没有办法遍历键,而不是 C++ 映射对?

【问题讨论】:

  • 获取值的迭代器的想法是在 STL 算法中使用它,例如,两个映射的键的交集。涉及 Boost 的解决方案不允许这样做,因为它会产生一个 Boost 迭代器。最差的答案获得最多的选票!

标签: c++ stl


【解决方案1】:

map 是关联容器。因此,迭代器是一对 key,val。如果您只需要键,则可以忽略该对中的值部分。

for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k =  iter->first;
//ignore value
//Value v = iter->second;
}

编辑:: 如果您只想向外部公开键,则可以将地图转换为矢量或键并公开。

【讨论】:

  • 但是将向量的迭代器暴露在外面真的是个坏主意。
  • 不要暴露迭代器。只需提供向量中的键
  • 您可能想要这样做:const Key&amp; k(iter-&gt;first);
  • 两件事,这回答了 OP 的问题,正是他已经知道但没有寻找的答案,其次,如果你想做类似的事情,这种方法对你没有帮助:std::vector&lt;Key&gt; v(myMap.begin(), myMap.end())
  • 不要将键转换为向量。制作一个新向量违背了迭代的目的,它应该是快速的并且不分配任何东西。此外,对于大型集,它会很慢。
【解决方案2】:

使用 C++11,迭代语法很简单。您仍然可以迭代对,但只访问密钥很容易。

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, int> myMap;

    myMap["one"] = 1;
    myMap["two"] = 2;
    myMap["three"] = 3;

    for ( const auto &myPair : myMap ) {
        std::cout << myPair.first << "\n";
    }
}

【讨论】:

  • 原始问题明确表示“不是对”。
  • 是的。但是您需要以某种方式获取数据。我正在说明一个有用的习惯用法。
【解决方案3】:

如果您确实需要隐藏“真实”迭代器返回的值(例如,因为您想将键迭代器与标准算法一起使用,以便它们对键而不是对进行操作),那么请使用看看 Boost 的transform_iterator

[提示:查看新类的 Boost 文档时,请先阅读末尾的“示例”。然后,您就有机会弄清楚其余部分到底在说什么:-)]

【讨论】:

【解决方案4】:

使用 C++17,您可以在 range-based for loop 中使用 structured binding(相应地调整 John H.'s answer):

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> myMap;

    myMap["one"] = 1;
    myMap["two"] = 2;
    myMap["three"] = 3;

    for ( const auto &[key, value]: myMap ) {
        std::cout << key << '\n';
    }
}

不幸的是,C++17 标准要求您声明 value 变量,即使您没有使用它(std::ignore 就像用于 std::tie(..) 一样不起作用,请参阅 this discussion)。

因此,某些编译器可能会警告您未使用的 value 变量!在我看来,关于未使用变量的编译时警告对于任何生产代码都是不可行的。因此,这可能不适用于某些编译器版本。

【讨论】:

  • 原则上你不能把它分配给std::ignore吗?这实际上会损害编译代码的效率,还是实际上不会评估? (我不是说在绑定中,而是作为循环中的一个动作)
  • 从 C++17 开始,您还可以使用 [[maybe_unused]]。这会抑制警告。像这样:for ([[maybe_unused]] const auto &amp;[key, v_not_used] : my_map) { use(key); }
  • Linters 和编译器通常会认为(void) value; 足够用而不抱怨,所以它也可以用来标记一个未使用的变量。
【解决方案5】:

没有增强

您可以通过简单地扩展该地图的 STL 迭代器来做到这一点。例如,字符串到整数的映射:

#include <map>
typedef map<string, int> ScoreMap;
typedef ScoreMap::iterator ScoreMapIterator;

class key_iterator : public ScoreMapIterator
{
  public:
    key_iterator() : ScoreMapIterator() {};
    key_iterator(ScoreMapIterator s) : ScoreMapIterator(s) {};
    string* operator->() { return (string* const)&(ScoreMapIterator::operator->()->first); }
    string operator*() { return ScoreMapIterator::operator*().first; }
};

您也可以perform this extension in a template,以获得更通用的解决方案。

您使用迭代器的方式与使用列表迭代器的方式完全相同,只是您要遍历地图的 begin()end()

ScoreMap m;
m["jim"] = 1000;
m["sally"] = 2000;

for (key_iterator s = m.begin(); s != m.end(); ++s)
    printf("\n key %s", s->c_str());

【讨论】:

  • +1:终于有人读到了“不是对”的部分!干杯,这节省了我研究规范的时间!
  • 在模板化解决方案下方,我添加了值迭代器。
  • 将您的问题与我的问题联系起来。
  • template&lt;typename C&gt; class key_iterator : public C::iterator
【解决方案6】:

下面是 Ian 提到的更通用的模板化解决方案...

#include <map>

template<typename Key, typename Value>
using Map = std::map<Key, Value>;

template<typename Key, typename Value>
using MapIterator = typename Map<Key, Value>::iterator;

template<typename Key, typename Value>
class MapKeyIterator : public MapIterator<Key, Value> {

public:

    MapKeyIterator ( ) : MapIterator<Key, Value> ( ) { };
    MapKeyIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };

    Key *operator -> ( ) { return ( Key * const ) &( MapIterator<Key, Value>::operator -> ( )->first ); }
    Key operator * ( ) { return MapIterator<Key, Value>::operator * ( ).first; }
};

template<typename Key, typename Value>
class MapValueIterator : public MapIterator<Key, Value> {

public:

    MapValueIterator ( ) : MapIterator<Key, Value> ( ) { };
    MapValueIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };

    Value *operator -> ( ) { return ( Value * const ) &( MapIterator<Key, Value>::operator -> ( )->second ); }
    Value operator * ( ) { return MapIterator<Key, Value>::operator * ( ).second; }
};

所有功劳归伊恩所有...谢谢伊恩。

【讨论】:

    【解决方案7】:

    你正在寻找map_keys,用它你可以写类似的东西

    BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys)
    {
      // do something with key
    }
    

    【讨论】:

    • BOOST_FOREACH(const key_t&amp; key, ...
    【解决方案8】:

    不用BOOST,直接使用key和value

    for(auto const& [key, value]: m_map)
        {
        std::cout<<" key="<<key;
        std::cout<<" value="<<value<<std::endl;
        }
    

    【讨论】:

    • 您好,欢迎来到 Stack Overflow!您能否简要解释一下迭代器正在做什么来帮助被问者理解?
    • 这正是问题作者所说的他们没有问的问题。
    【解决方案9】:

    这是一个使用 Boost 的 transform_iterator 的示例

    #include <iostream>
    #include <map>
    #include <iterator>
    #include "boost/iterator/transform_iterator.hpp"
    
    using std::map;
    typedef std::string Key;
    typedef std::string Val;
    
    map<Key,Val>::key_type get_key(map<Key,Val>::value_type aPair) {
      return aPair.first;
    }
    
    typedef map<Key,Val>::key_type (*get_key_t)(map<Key,Val>::value_type);
    typedef map<Key,Val>::iterator map_iterator;
    typedef boost::transform_iterator<get_key_t, map_iterator> mapkey_iterator;
    
    int main() {
      map<Key,Val> m;
      m["a"]="A";
      m["b"]="B";
      m["c"]="C";
    
      // iterate over the map's (key,val) pairs as usual
      for(map_iterator i = m.begin(); i != m.end(); i++) {
        std::cout << i->first << " " << i->second << std::endl;
      }
    
      // iterate over the keys using the transformed iterators
      mapkey_iterator keybegin(m.begin(), get_key);
      mapkey_iterator keyend(m.end(), get_key);
      for(mapkey_iterator i = keybegin; i != keyend; i++) {
        std::cout << *i << std::endl;
      }
    }
    

    【讨论】:

      【解决方案10】:

      当不需要显式的beginend 时,即对于范围循环,可以通过键(第一个示例)或值(第二个示例)进行循环

      #include <boost/range/adaptors.hpp>
      
      map<Key, Value> m;
      
      for (auto k : boost::adaptors::keys(m))
        cout << k << endl;
      
      for (auto v : boost::adaptors::values(m))
        cout << v << endl;
      

      【讨论】:

      • 应该在std中
      【解决方案11】:

      你想这样做吗?

      std::map<type,type>::iterator iter = myMap.begin();
      std::map<type,type>::iterator endIter = myMap.end();
      for(; iter != endIter; ++iter)
      {
         type key = iter->first;  
         .....
      }
      

      【讨论】:

      • 是的,我知道,问题是我有一个类 A { public: // 我想在此处公开私有映射键的迭代器 private: map };
      • 在这种情况下,我认为您可以使用 std::trasnform 创建一个 std::list 并仅从地图中获取键。然后您可以公开列表迭代器,因为向列表中插入更多元素不会使现有迭代器失效。
      【解决方案12】:

      如果您需要一个只返回键的迭代器,您需要将映射的迭代器包装在您自己的类中,以提供所需的接口。您可以像here 这样从头开始声明一个新的迭代器类,使用现有的帮助器构造。 This answer 展示了如何使用 Boost 的 transform_iterator 将迭代器包装在一个只返回值/键的迭代器中。

      【讨论】:

        【解决方案13】:

        你可以

        • 创建一个自定义迭代器类,聚合std::map&lt;K,V&gt;::iterator
        • 使用std::transformmap.begin()map.end() 使用 boost::bind( &amp;pair::second, _1 ) 函子
        • 在使用for 循环进行迭代时忽略-&gt;second 成员。

        【讨论】:

          【解决方案14】:

          除了没有BOOST_FOREACH 之外,这个答案就像rodrigob 的答案。你可以使用 c++ 的 range based for 来代替。

          #include <map>
          #include <boost/range/adaptor/map.hpp>
          #include <iostream>
          
          template <typename K, typename V>
          void printKeys(std::map<K,V> map){
               for(auto key : map | boost::adaptors::map_keys){
                    std::cout << key << std::endl;
               }
          }
          

          【讨论】:

            【解决方案15】:

            使用 C++20,我们可以访问 ranges 库,它为此提供了一个很好的解决方案:std::views::keys

            #include <ranges>
            
            //...
            
            std::map<int, int> myMap = {{1,2},{3,4},{5,6}};
            auto keys = std::views::keys(myMap);
            for(auto key : keys) {
                std::cout << key << std::endl;
            }
            

            自己尝试一下:https://godbolt.org/z/heeWv4Gh6

            【讨论】:

              【解决方案16】:

              如果没有 Boost,您可以这样做。如果你能写一个强制转换操作符而不是 getKeyIterator(),那就太好了,但我无法编译它。

              #include <map>
              #include <unordered_map>
              
              
              template<typename K, typename V>
              class key_iterator: public std::unordered_map<K,V>::iterator {
              
              public:
              
                  const K &operator*() const {
                      return std::unordered_map<K,V>::iterator::operator*().first;
                  }
              
                  const K *operator->() const {
                      return &(**this);
                  }
              };
              
              template<typename K,typename V>
              key_iterator<K,V> getKeyIterator(typename std::unordered_map<K,V>::iterator &it) {
                  return *static_cast<key_iterator<K,V> *>(&it);
              }
              
              int _tmain(int argc, _TCHAR* argv[])
              {
                  std::unordered_map<std::string, std::string> myMap;
                  myMap["one"]="A";
                  myMap["two"]="B";
                  myMap["three"]="C";
                  key_iterator<std::string, std::string> &it=getKeyIterator<std::string,std::string>(myMap.begin());
                  for (; it!=myMap.end(); ++it) {
                      printf("%s\n",it->c_str());
                  }
              }
              

              【讨论】:

                【解决方案17】:

                为了后代,由于我试图找到一种方法来创建一个范围,另一种方法是使用boost::adaptors::transform

                这是一个小例子:

                #include <boost/range/adaptor/transformed.hpp>
                #include <iostream>
                #include <map>
                
                int main(int argc, const char* argv[])
                {
                  std::map<int, int> m;
                  m[0]  = 1;
                  m[2]  = 3;
                  m[42] = 0;
                
                  auto key_range =
                    boost::adaptors::transform(
                      m,
                      [](std::map<int, int>::value_type const& t) 
                      { return t.first; }
                    ); 
                  for (auto&& key : key_range)
                    std::cout << key << ' ';
                  std::cout << '\n';
                  return 0;
                }
                

                如果您想遍历这些值,请在 lambda 中使用 t.second

                【讨论】:

                  【解决方案18】:

                  这里有很多好的答案,下面是一种使用其中几个的方法,可以让你写这个:

                  void main()
                  {
                      std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
                      for (auto key : MapKeys(m))
                          std::cout << key << std::endl;
                  }
                  

                  如果这是你一直想要的,那么这里是 MapKeys() 的代码:

                  template <class MapType>
                  class MapKeyIterator {
                  public:
                      class iterator {
                      public:
                          iterator(typename MapType::iterator it) : it(it) {}
                          iterator operator++() { return ++it; }
                          bool operator!=(const iterator & other) { return it != other.it; }
                          typename MapType::key_type operator*() const { return it->first; }  // Return key part of map
                      private:
                          typename MapType::iterator it;
                      };
                  private:
                      MapType& map;
                  public:
                      MapKeyIterator(MapType& m) : map(m) {}
                      iterator begin() { return iterator(map.begin()); }
                      iterator end() { return iterator(map.end()); }
                  };
                  template <class MapType>
                  MapKeyIterator<MapType> MapKeys(MapType& m)
                  {
                      return MapKeyIterator<MapType>(m);
                  }
                  

                  【讨论】:

                    【解决方案19】:

                    我已采用 Ian 的回答来处理所有地图类型,并修复了返回 operator* 的引用

                    template<typename T>
                    class MapKeyIterator : public T
                    {
                    public:
                        MapKeyIterator() : T() {}
                        MapKeyIterator(T iter) : T(iter) {}
                        auto* operator->()
                        {
                            return &(T::operator->()->first);
                        }
                        auto& operator*()
                        {
                            return T::operator*().first;
                        }
                    };
                    

                    【讨论】:

                      【解决方案20】:

                      我知道这并不能回答您的问题,但您可能想要查看的一个选项是仅将两个具有相同索引的向量作为“链接”信息。

                      所以在..

                      std::vector<std::string> vName;
                      
                      std::vector<int> vNameCount;
                      

                      如果您想要按名称计算名称,您只需对 vName.size() 进行快速 for 循环,当您找到它时,这就是您要查找的 vNameCount 的索引。

                      当然,这可能无法为您提供地图的所有功能,并且取决于可能会或可能不会更好,但如果您不知道密钥,可能会更容易,并且不应该添加太多处理。

                      请记住,当您从一个添加/删除时,您必须从另一个添加/删除,否则事情会变得疯狂嘿:P

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2021-04-15
                        • 2011-07-31
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-08-06
                        相关资源
                        最近更新 更多