[英]how can I get a std::set of keys to a std::map
今天早上我正在寫一個算法,我遇到了一個奇怪的情況。 我有兩個std::map
。 我想對每個鍵的集合執行集合交集(以查找兩個映射共有的鍵)。 在未來的某個時候,我想我很可能也想在這里執行集合減法。 幸運的是,STL 包含這兩種操作的功能。 問題是,我似乎無法從std::map
中得到一個std::set
鍵。 有沒有辦法做到這一點? 我正在尋找這樣簡單的東西,就像在 Java 中一樣:
std::set<Foo> keys = myMap.getKeySet();
我的理解是,我不能直接在映射中的迭代器上使用std::set_intersection()
函數,因為映射公開std::pair
對象而不僅僅是鍵。 另外,我認為地圖不能保證秩序。 我也有興趣在一對std::multimap
上執行相同的操作,如果這有什么不同的話。
編輯:我最初忘了提到,由於我被迫使用的編譯器的年齡(MSVC++ 6),大多數在 boost 中可用的漂亮模板技巧都不能使用。
你基本上想要的是一個副本,因為std :: map不會將鍵保存在std :: set中。 std :: copy假定值類型是兼容的,這不是這里的情況。 std :: map :: value_type是一個std :: pair。 您只想復制該對的第一部分,這意味着您需要一個std :: transform。 現在,由於您將在集合上使用insert_iterator,因此順序無關緊要。 即使地圖已經排序,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。
[edit]更新了C ++ 14
std::transform(MyMap.begin(), MyMap.end(),
std::inserter(MySet, MySet.end()),
[](auto pair){ return pair.first; });
地圖確實保證秩序; 這就是為什么它被稱為有序關聯容器 。 您可以將set_intersection與自定義比較器功能一起使用,這是此處列出的第二個變體。
所以,像
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比較兩個組件。 由於您只需要在對的第一部分(映射鍵)上進行等效,因此您需要定義自己的比較運算符來提供這種關系(這是上面的函數所做的)。
您可以使用通用的boost :: transform_iterator返回僅返回鍵(而不是值)的迭代器。 請參閱如何從std :: map中檢索所有鍵(或值)並將它們放入向量中?
在實踐中,
yourmap::const_iterator mi;
set<key_type> k;
for (mi = yourmap.begin(); mi != yourmap.end(); ++mi)
k.insert(mi->first);
return k;
最好的非SGI,非升級STL算法友好的解決方案是擴展map :: iterator,如下所示:
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));
您可以迭代並將每個鍵添加到集合中。 集合和映射都是有序的,無序變量不是。
我在這里找到了你的問題的良好鏈接
並為您的問題提供一些代碼:
#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;
}
您也許可以為地圖創建一個迭代器,它只使用boost :: adapters :: map_key生成鍵,請參閱boost :: adapters :: map_key文檔中的示例。 這似乎是在Boost 1.43中引入的,並且應該與C ++ 2003兼容,但我不知道VC ++ 6是否具體,這是來自C ++ 98時代。
從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編譯。
我有同樣的問題。 我通過使用函數模板解決了它
template <class T, class U>
set<T> getSetFromKeys(map<T,U> mapWithKeys){
set<T> outputSet;
for (pair<const T, U> keyValuePair : mapWithKeys) {
outputSet.insert(keyValuePair.first);
}
return outputSet;
}
通過這種方式,我有一種從應用程序中的映射中提取鍵的通用方法。
正如 MSalters 所說,您可能只想創建一組密鑰。 這對於小地圖來說可能已經足夠了。
一個快速的方法是:
std::set<Foo> keys;
for (auto &iter : myMap)
{
keys.insert(iter.first);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.