在 C + + 映射中迭代键

有没有一种方法可以迭代键,而不是 C + + 映射的对?

239384 次浏览

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

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

编辑: 如果您只想向外部公开键,那么您可以将映射转换为向量或键并公开。

你想这么做吗?

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

如果您真的需要隐藏“真正的”迭代器返回的值(例如,因为您想在标准算法中使用键迭代器,以便它们操作键而不是对) ,那么请看 Boost 的 转换迭代器

[提示: 当查看新类的 Boost 文档时,首先阅读结尾的“示例”。然后,你就有机会搞清楚其余部分究竟在谈论什么:

你可以的

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

如果您需要一个只返回键的迭代器,那么您需要将 map 的迭代器包装到您自己的类中,该类提供所需的接口。您可以从头声明一个新的迭代器类,比如使用现有的帮助器构造的 给你这个答案展示了如何使用 Boost 的 transform_iterator将迭代器包装在一个只返回值/键的迭代器中。

我知道这并不能回答你的问题,但是你可能想看到的一个选项是,只有两个具有相同索引的向量被“链接”信息。.

所以..。

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


std::vector<int> vNameCount;

如果您想要按名称计数,只需在 vName.size ()上执行快速 for 循环,当您找到它时,它就是您正在寻找的 vNameCount 的索引。

当然,这可能不会给你所有的功能的地图,并取决于可能或可能不是更好,但它可能会更容易,如果你不知道的关键,不应该添加太多的处理。

只要记住,当你添加/删除一个你必须做它从另一个否则事情会变得疯狂嘿: P

下面是一个如何使用 Boost 的 转换迭代器实现的示例

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

你正在寻找 Map _ keys,用它你可以写东西像

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

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

没有 Boost

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

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

您也可以使用 在模板中执行此扩展,以获得更通用的解决方案。

您使用迭代器的方式与使用列表迭代器的方式完全一样,只不过是在映射的 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());

使用 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";
}
}

这个答案和 rodrigob 的一样,只是没有 BOOST_FOREACH。你可以用 c + + 的范围来代替。

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

在伊恩提到的更一般的模板解决方案下面..。

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

所有学分归 Ian... 谢谢 Ian。

对于子孙后代来说,由于我试图找到一种方法来创建一个范围,另一种选择是使用 适配器: : 转换

举个小例子:

#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

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

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

当不需要显式的 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;

使用 C + + 17 ,你可以在 基于范围的循环中使用 结构化绑定结构化绑定(相应地调整 John H 的回答) :

#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(..)的一种使用方法是不起作用的,请参阅 这个讨论)。

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

我已经采用了伊恩的回答与所有地图类型的工作和固定返回一个 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;
}
};

没有 BOOST,直接使用键和值

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

使用 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