95

現在Effective STLに取り組んでいます。項目 5 は、単一要素の対応するメンバー関数よりも範囲メンバー関数を使用する方が通常は望ましいことを示唆しています。現在、マップ内のすべての値をベクターにコピーしたいと考えています (つまり、キーは必要ありません)。

これを行う最もクリーンな方法は何ですか?

4

13 に答える 13

71

おそらくstd::transformその目的で使用できます。より読みやすいものに応じて、Neilsバージョンを好むかもしれません。


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

非常に一般的です。役立つと思われる場合は、彼にクレジットを与えることを忘れないでください。

于 2009-04-21T07:42:50.300 に答える
64

マップから取得したイテレータは 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 );
    }
}
于 2009-04-21T07:37:00.157 に答える
37

C++11 では、派手な新しいforループがあります。

for (const auto &s : schemas)
   names.push_back(s.second);

は でschemasありstd::mapnamesstd::vectorです。

これにより、配列 ( names) にマップ ( schemas) の値が入力されます。に変更s.seconds.firstて、キーの配列を取得します。

于 2015-12-29T22:04:53.307 に答える
32
#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のすべてのアイテムに対して呼び出します。operation の値は に格納されます。inputIteratorbeginInputRangeendInputRangeoutputIterator

マップ全体を操作したい場合は、入力範囲として map.begin() と map.end() を使用します。マップ値をベクターに保存したいので、ベクターで back_inserter を使用する必要がありますback_inserter(your_values_vector)。back_inserter は、指定された (パラメーターとして) コレクションの最後に新しい要素をプッシュする特別な outputIterator です。最後のパラメーターは unaryOperation です。1 つのパラメーター (inputIterator の値) のみを取ります。したがって、 lambda: を使用できます [](auto &kv) { [...] }。 &kv は、マップ アイテムのペアへの単なる参照です。したがって、マップのアイテムの値のみを返したい場合は、単純に kv.second を返すことができます。

[](auto &kv) { return kv.second; }

これで疑問が解けると思います。

于 2016-09-23T07:52:05.933 に答える
24

ブースト ライブラリを使用している場合は、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) );
}

このソリューションは、 boost メーリング リストの Michael Goldshteyn の投稿に基づいています。

于 2010-04-09T14:42:18.177 に答える
20

ラムダを使用すると、次のことができます。

{
   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); });
}
于 2009-04-21T13:04:42.617 に答える
9

これが私がすることです。
また、テンプレート関数を使用して、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)
                  );
}
于 2009-04-21T08:37:23.583 に答える
2

あるべきだと思った

std::transform( map.begin(), map.end(), 
                   std::back_inserter(vec), 
                   boost::bind(&MapT::value_type::first,_1) ); 
于 2010-05-03T12:37:23.840 に答える
2

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);
}
于 2009-04-21T07:49:43.653 に答える
1

他の回答はstd::transformに言及しており、意味的には正しい選択です。しかし、実際にはstd::accumulateがこのタスクにより適している可能性があります。

  • 結果のベクトルにconstを追加できます。
  • 見た目が良く、本当に機能的なスタイルです。

例 (C++17 構文を使用):

#include <numeric> // for std::accumulate. Note that it's not in <algorithm> where std::transform is located, thanks to Anton Krug for pointing this out

auto map = std::map<int,bool>{};
map[0]=true;
map[1]=false;

const auto mapValues = std::accumulate(map.begin(), map.end(), std::vector<bool>(map.size()), [](auto& vector, const auto& mapEntry) {
    vector.push_back(mapEntry.second);
    return vector;
});
于 2021-04-15T13:19:26.330 に答える