Có cách nào để lặp lại các khóa, không phải các cặp của bản đồ C ++ không?
Có cách nào để lặp lại các khóa, không phải các cặp của bản đồ C ++ không?
Câu trả lời:
Nếu bạn thực sự cần ẩn giá trị mà trình vòng lặp "thực" trả về (ví dụ: vì bạn muốn sử dụng trình vòng lặp khóa của mình với các thuật toán tiêu chuẩn, để chúng hoạt động trên các phím thay vì các cặp), thì hãy xem Boost's biến_ngoài .
[Mẹo: khi xem tài liệu về Boost cho một lớp mới, trước tiên hãy đọc "ví dụ" ở cuối. Sau đó, bạn có cơ hội thể thao để tìm hiểu xem phần còn lại của nó đang nói về cái quái gì :-)]
bản đồ là vùng chứa kết hợp. Do đó, trình lặp là một cặp khóa, val. NẾU bạn chỉ cần khóa, bạn có thể bỏ qua phần giá trị từ cặp.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
CHỈNH SỬA:: Trong trường hợp bạn chỉ muốn để lộ các phím ra bên ngoài thì bạn có thể chuyển đổi bản đồ thành vectơ hoặc các phím và hiển thị.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
Với C ++ 11, cú pháp lặp rất đơn giản. Bạn vẫn lặp lại các cặp, nhưng chỉ cần truy cập vào khóa là dễ dàng.
#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";
}
}
Bạn có thể làm điều này bằng cách mở rộng trình lặp STL cho bản đồ đó. Ví dụ: ánh xạ chuỗi thành 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; }
};
Bạn cũng có thể thực hiện tiện ích mở rộng này trong một mẫu để có giải pháp chung hơn.
Bạn sử dụng trình vòng lặp của mình giống hệt như bạn sử dụng trình vòng lặp danh sách, ngoại trừ việc bạn đang lặp lại trên bản đồ begin()
và 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
, v.v.
Với C ++ 17, bạn có thể sử dụng liên kết có cấu trúc bên trong vòng lặp for dựa trên phạm vi (điều chỉnh câu trả lời của John H. cho phù hợp):
#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';
}
}
Thật không may, tiêu chuẩn C ++ 17 yêu cầu bạn khai báo value
biến, ngay cả khi bạn không sử dụng nó ( std::ignore
vì một biến sẽ sử dụng cho std::tie(..)
không hoạt động, hãy xem thảo luận này ).
Do đó, một số trình biên dịch có thể cảnh báo bạn về value
biến không sử dụng ! Cảnh báo thời gian biên dịch liên quan đến các biến không sử dụng là điều không nên đối với bất kỳ mã sản xuất nào trong tâm trí tôi. Vì vậy, điều này có thể không áp dụng cho một số phiên bản trình biên dịch nhất định.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Dưới đây là giải pháp mẫu tổng quát hơn mà Ian đã giới thiệu ...
#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; }
};
Tất cả các khoản tín dụng được chuyển cho Ian ... Cảm ơn Ian.
Đây là một ví dụ về cách thực hiện bằng cách sử dụng biến đổi của 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;
}
}
Khi không cần rõ ràng begin
và end
cần thiết, tức là đối với vòng lặp phạm vi, vòng lặp qua các khóa (ví dụ đầu tiên) hoặc các giá trị (ví dụ thứ hai) có thể nhận được với
#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;
Bạn muốn làm điều này?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
Nếu bạn cần một trình vòng lặp chỉ trả về các khóa bạn cần để bọc trình vòng lặp của bản đồ trong lớp của riêng bạn cung cấp giao diện mong muốn. Bạn có thể khai báo một lớp trình lặp mới từ đầu như ở đây , sử dụng các cấu trúc trình trợ giúp hiện có. Câu trả lời này cho thấy cách sử dụng Boost transform_iterator
để bọc trình vòng lặp trong một trình chỉ trả về các giá trị / khóa.
Câu trả lời này giống như rodrigob ngoại trừ không có BOOST_FOREACH
. Bạn có thể sử dụng phạm vi của c ++ để thay thế.
#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;
}
}
Nếu không có Boost, bạn có thể làm như thế này. Sẽ rất tuyệt nếu bạn có thể viết toán tử ép kiểu thay vì getKeyIterator (), nhưng tôi không thể biên dịch nó.
#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());
}
}
Đối với hậu thế, và vì tôi đang cố gắng tìm cách tạo ra một phạm vi, một giải pháp thay thế là sử dụng boost :: adapter :: biến đổi
Đây là một ví dụ nhỏ:
#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;
}
Nếu bạn muốn lặp lại các giá trị, hãy sử dụng t.second
trong lambda.
Rất nhiều câu trả lời hay ở đây, dưới đây là một cách tiếp cận sử dụng một vài trong số chúng cho phép bạn viết điều này:
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
Nếu đó là những gì bạn luôn muốn, thì đây là mã cho 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);
}
Tôi đã chấp nhận câu trả lời của Ian để làm việc với tất cả các loại bản đồ và đã sửa trả lại một tham chiếu cho 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;
}
};
Tôi biết điều này không trả lời được câu hỏi của bạn, nhưng một tùy chọn bạn có thể muốn xem xét là chỉ có hai vectơ có cùng chỉ mục là thông tin "được liên kết" ..
Vì vậy, trong ..
std::vector<std::string> vName;
std::vector<int> vNameCount;
nếu bạn muốn đếm tên theo tên, bạn chỉ cần thực hiện nhanh vòng lặp for qua vName.size () và khi bạn tìm thấy đó là chỉ mục cho vNameCount mà bạn đang tìm kiếm.
Chắc chắn rằng điều này có thể không cung cấp cho bạn tất cả các chức năng của bản đồ và tùy thuộc vào có thể tốt hơn hoặc có thể tốt hơn, nhưng có thể dễ dàng hơn nếu bạn không biết các phím và không nên thêm quá nhiều xử lý.
Chỉ cần nhớ khi bạn thêm / xóa từ cái này, bạn phải làm điều đó từ cái kia nếu không mọi thứ sẽ trở nên điên rồ heh: P