web-dev-qa-db-ja.com

std :: mapへのキーのstd :: setを取得する方法

私は今朝アルゴリズムを書いていて、不思議な状況に出くわしました。 2つの_std::map_ sがあります。それぞれのキーのセットに対してセット交差を実行したい(どのキーが両方のマップに共通であるかを見つけるため)。将来のある時点で、ここでもセット減算も実行したいと思うでしょう。幸いなことに、STLにはこれらの操作の両方の機能が含まれています。問題は、_std::set_からキーの_std::map_を取得できないようです。これを行う方法はありますか?私はそれがJavaのようにこれほど単純なものを探しています:

_std::set<Foo> keys = myMap.getKeySet();
_

マップはキーだけでなく_std::pair_オブジェクトを公開するため、マップへのイテレータでstd::set_intersection()関数を直接使用できないことを理解しています。また、地図が順序を保証しているとは思いません。 _std::multimap_ sのペアで同じ操作を実行することにも興味があります。

[〜#〜] edit [〜#〜]:最初に言及するのを忘れていましたが、コンパイラーの古さのため、私は(MSVC++ 6)を使用せざるを得ませんでした。ブーストでのご利用はできません。

34
rmeador

用途の広いboost :: transform_iteratorを使用して、(値ではなく)キーのみを返すイテレータを返すことができます。参照 std :: mapからすべてのキー(または値)を取得してベクターに入れる方法

8
amit

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は必要ありません。

[編集] C++ 14用に更新

std::transform(MyMap.begin(), MyMap.end(),
    std::inserter(MySet, MySet.end()),
    [](auto pair){ return pair.first; });
17
MSalters

マップdoes保証順序;それが ソートされた連想コンテナ と呼ばれる理由です。リスト2番目のバリアント here であるカスタムコンパレーター関数で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ペアは、両方のコンポーネントを比較します。ペアの最初の部分(マップキー)についてのみ等価が必要なので、そのような関係を提供する独自の比較演算子を定義する必要があります(これは上記の関数が行うことです)。

12
zvrba

実際には、

yourmap::const_iterator mi;
set<key_type> k;
for (mi = yourmap.begin(); mi != yourmap.end(); ++mi)
  k.insert(mi->first);
return k; 
9
Antti Huima

非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));
4
Marius

私はあなたの質問に良いリンクを見つけました ここ

そしてあなたの問題のためのいくつかのコードがあります:

    #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;
    }
2

反復して、各キーをセットに追加するだけです。セットとマップは両方とも順序付けられていますが、順序付けされていないバリアントはそうではありません。

2
rlbond

おそらく、boost :: adapters :: map_keyを使用してキーのみを生成するマップのイテレータを作成できます。 boost :: adapters :: map_keyのドキュメント の例を参照してください。これはBoost 1.43で導入されたようで、C++ 2003と互換性があると思われますが、特にC++ 98時代のVC++ 6については知りません。

1
YitzikC

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

したがって、中間のコピーやセットを作成しなくても、2つのマップの交差部分を効率的に取得できます。この構成はgcc5.3でコンパイルされます。

1
ivannaz