【发布时间】:2010-11-29 10:49:12
【问题描述】:
有没有办法遍历键,而不是 C++ 映射对?
【问题讨论】:
-
获取值的迭代器的想法是在 STL 算法中使用它,例如,两个映射的键的交集。涉及 Boost 的解决方案不允许这样做,因为它会产生一个 Boost 迭代器。最差的答案获得最多的选票!
有没有办法遍历键,而不是 C++ 映射对?
【问题讨论】:
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& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())。
使用 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";
}
}
【讨论】:
如果您确实需要隐藏“真实”迭代器返回的值(例如,因为您想将键迭代器与标准算法一起使用,以便它们对键而不是对进行操作),那么请使用看看 Boost 的transform_iterator。
[提示:查看新类的 Boost 文档时,请先阅读末尾的“示例”。然后,您就有机会弄清楚其余部分到底在说什么:-)]
【讨论】:
使用 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 变量!在我看来,关于未使用变量的编译时警告对于任何生产代码都是不可行的。因此,这可能不适用于某些编译器版本。
【讨论】:
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
(void) value; 足够用而不抱怨,所以它也可以用来标记一个未使用的变量。
您可以通过简单地扩展该地图的 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());
【讨论】:
template<typename C> class key_iterator : public C::iterator等
下面是 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; }
};
所有功劳归伊恩所有...谢谢伊恩。
【讨论】:
你正在寻找map_keys,用它你可以写类似的东西
BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys)
{
// do something with key
}
【讨论】:
BOOST_FOREACH(const key_t& key, ...
for(auto const& [key, value]: m_map)
{
std::cout<<" key="<<key;
std::cout<<" value="<<value<<std::endl;
}
【讨论】:
这是一个使用 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;
}
}
【讨论】:
当不需要显式的begin 和end 时,即对于范围循环,可以通过键(第一个示例)或值(第二个示例)进行循环
#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::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator endIter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
【讨论】:
如果您需要一个只返回键的迭代器,您需要将映射的迭代器包装在您自己的类中,以提供所需的接口。您可以像here 这样从头开始声明一个新的迭代器类,使用现有的帮助器构造。 This answer 展示了如何使用 Boost 的 transform_iterator 将迭代器包装在一个只返回值/键的迭代器中。
【讨论】:
你可以
std::map<K,V>::iterator
std::transform 的map.begin() 到map.end()
使用 boost::bind( &pair::second, _1 ) 函子for 循环进行迭代时忽略->second 成员。【讨论】:
除了没有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;
}
}
【讨论】:
使用 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;
}
【讨论】:
如果没有 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::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。
【讨论】:
这里有很多好的答案,下面是一种使用其中几个的方法,可以让你写这个:
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);
}
【讨论】:
我已采用 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;
}
};
【讨论】:
我知道这并不能回答您的问题,但您可能想要查看的一个选项是仅将两个具有相同索引的向量作为“链接”信息。
所以在..
std::vector<std::string> vName;
std::vector<int> vNameCount;
如果您想要按名称计算名称,您只需对 vName.size() 进行快速 for 循环,当您找到它时,这就是您要查找的 vNameCount 的索引。
当然,这可能无法为您提供地图的所有功能,并且取决于可能会或可能不会更好,但如果您不知道密钥,可能会更容易,并且不应该添加太多处理。
请记住,当您从一个添加/删除时,您必须从另一个添加/删除,否则事情会变得疯狂嘿:P
【讨论】: