現在、EffectiveSTLを使用しています。項目5は、通常、対応する単一要素よりも範囲メンバー関数を使用する方が望ましいことを示しています。私は現在、マップ内のすべての値をベクトルにコピーしたいと思っています(つまり、キーは必要ありません)。
これを行う最もクリーンな方法は何ですか?
回答:
マップから取得したイテレータはstd :: pairを参照するため、ここで範囲を簡単に使用することはできません。ここで、ベクターに挿入するために使用するイテレータは、ベクターに格納されているタイプのオブジェクトを参照します。 (キーを破棄する場合)ペアではありません。
私はそれが明白なものよりもはるかにきれいになるとは本当に思いません:
#include <map>
#include <vector>
#include <string>
using namespace std;
int main() {
typedef map <string, int> MapType;
MapType m;
vector <int> v;
// populate map somehow
for( MapType::iterator it = m.begin(); it != m.end(); ++it ) {
v.push_back( it->second );
}
}
これを複数回使用する場合は、おそらくテンプレート関数として書き直します。何かのようなもの:
template <typename M, typename V>
void MapToVec( const M & m, V & v ) {
for( typename M::const_iterator it = m.begin(); it != m.end(); ++it ) {
v.push_back( it->second );
}
}
あなたはおそらくstd::transform
その目的のために使うことができます。読みやすいものによっては、ニールズバージョンの方がいいかもしれません。
xtoflによる例(コメントを参照):
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
template< typename tPair >
struct second_t {
typename tPair::second_type operator()( const tPair& p ) const { return p.second; }
};
template< typename tMap >
second_t< typename tMap::value_type > second( const tMap& m ) { return second_t< typename tMap::value_type >(); }
int main() {
std::map<int,bool> m;
m[0]=true;
m[1]=false;
//...
std::vector<bool> v;
std::transform( m.begin(), m.end(), std::back_inserter( v ), second(m) );
std::transform( m.begin(), m.end(), std::ostream_iterator<bool>( std::cout, ";" ), second(m) );
}
非常に一般的です。役立つと思われる場合は、彼にクレジットを与えることを忘れないでください。
古い質問、新しい答え。C ++ 11には、新しいforループがあります。
for (const auto &s : schemas)
names.push_back(s.first);
ここで、schemasはa std::map
、namesはstd::vector
。です。
これにより、配列(名前)にマップ(スキーマ)のキーが入力されます。に変更s.first
しs.second
て、値の配列を取得します。
const auto &s
reserve()
と、パフォーマンスがさらに向上します。C ++ 11の登場により、これが受け入れられるソリューションになるはずです。
ブーストライブラリを使用している場合は、boost :: bindを使用して、次のようにペアの2番目の値にアクセスできます。
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
int main()
{
typedef std::map<std::string, int> MapT;
typedef std::vector<int> VecT;
MapT map;
VecT vec;
map["one"] = 1;
map["two"] = 2;
map["three"] = 3;
map["four"] = 4;
map["five"] = 5;
std::transform( map.begin(), map.end(),
std::back_inserter(vec),
boost::bind(&MapT::value_type::second,_1) );
}
このソリューションは、ブーストメーリングリストのMichaelGoldshteynからの投稿に基づいています。
#include <algorithm> // std::transform
#include <iterator> // std::back_inserter
std::transform(
your_map.begin(),
your_map.end(),
std::back_inserter(your_values_vector),
[](auto &kv){ return kv.second;}
);
説明を追加しなかったのは残念です。コードは非常に単純なので、説明は必要ないと思いました。そう:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
この関数はunaryOperation
、inputIterator
範囲(beginInputRange
- endInputRange
)のすべてのアイテムを呼び出します。操作の値はに格納されoutputIterator
ます。
マップ全体を操作する場合は、入力範囲としてmap.begin()とmap.end()を使用します。マップ値をベクターに保存したいので、ベクターでback_inserterを使用する必要がありますback_inserter(your_values_vector)
。back_inserterは、指定された(パラメーターとして)コレクションの最後に新しい要素をプッシュする特別なoutputIteratorです。最後のパラメーターはunaryOperationです-それは1つのパラメーターのみを取ります-inputIteratorの値。したがって[](auto &kv) { [...] }
、lambda:を使用できます。
ここで、&kvはマップアイテムのペアへの単なる参照です。したがって、マップのアイテムの値のみを返したい場合は、単にkv.secondを返すことができます。
[](auto &kv) { return kv.second; }
これは疑問を説明していると思います。
ラムダを使用すると、次のことが実行できます。
{
std::map<std::string,int> m;
std::vector<int> v;
v.reserve(m.size());
std::for_each(m.begin(),m.end(),
[&v](const std::map<std::string,int>::value_type& p)
{ v.push_back(p.second); });
}
これが私がすることです。
また、テンプレート関数を使用して、select2ndの構築を容易にします。
#include <map>
#include <vector>
#include <algorithm>
#include <memory>
#include <string>
/*
* A class to extract the second part of a pair
*/
template<typename T>
struct select2nd
{
typename T::second_type operator()(T const& value) const
{return value.second;}
};
/*
* A utility template function to make the use of select2nd easy.
* Pass a map and it automatically creates a select2nd that utilizes the
* value type. This works nicely as the template functions can deduce the
* template parameters based on the function parameters.
*/
template<typename T>
select2nd<typename T::value_type> make_select2nd(T const& m)
{
return select2nd<typename T::value_type>();
}
int main()
{
std::map<int,std::string> m;
std::vector<std::string> v;
/*
* Please note: You must use std::back_inserter()
* As transform assumes the second range is as large as the first.
* Alternatively you could pre-populate the vector.
*
* Use make_select2nd() to make the function look nice.
* Alternatively you could use:
* select2nd<std::map<int,std::string>::value_type>()
*/
std::transform(m.begin(),m.end(),
std::back_inserter(v),
make_select2nd(m)
);
}
1つの方法は、ファンクターを使用することです。
template <class T1, class T2>
class CopyMapToVec
{
public:
CopyMapToVec(std::vector<T2>& aVec): mVec(aVec){}
bool operator () (const std::pair<T1,T2>& mapVal) const
{
mVec.push_back(mapVal.second);
return true;
}
private:
std::vector<T2>& mVec;
};
int main()
{
std::map<std::string, int> myMap;
myMap["test1"] = 1;
myMap["test2"] = 2;
std::vector<int> myVector;
//reserve the memory for vector
myVector.reserve(myMap.size());
//create the functor
CopyMapToVec<std::string, int> aConverter(myVector);
//call the functor
std::for_each(myMap.begin(), myMap.end(), aConverter);
}
何故なの:
template<typename K, typename V>
std::vector<V> MapValuesAsVector(const std::map<K, V>& map)
{
std::vector<V> vec;
vec.reserve(map.size());
std::for_each(std::begin(map), std::end(map),
[&vec] (const std::map<K, V>::value_type& entry)
{
vec.push_back(entry.second);
});
return vec;
}
使用法:
自動vec = MapValuesAsVector(anymap);
STLアルゴリズムの変換関数を使用する必要があります。変換関数の最後のパラメーターは、関数オブジェクト、関数ポインター、またはマップのアイテムをベクトルのアイテムに変換するラムダ関数です。このケースマップには、vectorのint型を持つアイテムに変換する必要があるタイプペアを持つアイテムがあります。これがラムダ関数を使用する私の解決策です:
#include <algorithm> // for std::transform
#include <iterator> // for back_inserted
// Map of pair <int, string> need to convert to vector of string
std::map<int, std::string> mapExp = { {1, "first"}, {2, "second"}, {3, "third"}, {4,"fourth"} };
// vector of string to store the value type of map
std::vector<std::string> vValue;
// Convert function
std::transform(mapExp.begin(), mapExp.end(), std::back_inserter(vValue),
[](const std::pair<int, string> &mapItem)
{
return mapItem.second;
});