マップ用に独自のコンパレータを作成するにはどうすればよいですか?


83
typedef map<string, string> myMap;

に新しいペアを挿入するmyMapと、キーstringを使用して独自の文字列コンパレータで比較します。そのコンパレータをオーバーライドすることは可能ですか?たとえば、キーstringをアルファベットではなく長さで比較したいと思います。または、マップを並べ替える他の方法はありますか?

回答:


140

std::map最大4つのテンプレートタイプ引数を取り、3番目はコンパレータです。例えば:

struct cmpByStringLength {
    bool operator()(const std::string& a, const std::string& b) const {
        return a.length() < b.length();
    }
};

// ...
std::map<std::string, std::string, cmpByStringLength> myMap;

または、コンパレータをmapコンストラクタに渡すこともできます。

ただし、長さで比較する場合、キーとしてマップ内の各長さの文字列を1つだけ持つことができることに注意してください。


4
重複したキーを含める場合は、マルチマップを使用できることに注意してください
Xitrum 2011

@GeorgFritzsche比較子をコンストラクターに渡す例を提供する可能性はありますか?
bpeikes 2014年

1
@bpeikes:見た目はそれほど変わりません:std::map<std::string, std::string> myMap(cmpByStringLength());
Georg Fritzsche

std :: map <int、int>に問題があり、順序が上がるものと降るものがありました。std :: map <int、int、std :: greater>とstd :: map <int、int、std :: less>を使いたくなかったのは、異なる順序で並べ替えられたマップを使用できなかったためです。すべてをテンプレートにしない限り、単一の関数のパラメーターとして。次のことを行う必要があることがわかりました。typedefstd:: map <int、int、(bool)*(int、int)> mymap; その後、関数を渡すことができました。次のことを試しましたが、機能しませんでした。typedefstd :: map <int、int> mymap; mymap map1(std :: less); mymap map2(std :: greater);
bpeikes 2014年

2
@GeorgFritzsche:コンストラクター引数はコンパレーター型のインスタンスである必要があり、のインスタンスでcmpByStringLengthはないため、コンストラクターにコンパレーターを渡す場合は機能しませんstd::less<std::string>。コンストラクターに任意のコンパレーターを設定できる一般的なマップの場合、次のようなものが必要ですstd::map<std::string, std::string, std::function<bool(const std::string &, const std::string &)>> myMap(cmpByStringLength);
Chris Dodd

19

C ++ 11以降、コンパレータ構造体を定義する代わりにラムダ式を使用することもできます。

auto comp = [](const string& a, const string& b) { return a.length() < b.length(); };
map<string, string, decltype(comp)> my_map(comp);

my_map["1"]      = "a";
my_map["three"]  = "b";
my_map["two"]    = "c";
my_map["fouuur"] = "d";

for(auto const &kv : my_map)
    cout << kv.first << endl;

出力:

1
2
3
fouuur

Georgの答えの最後のメモを繰り返したいと思います。長さで比較する場合、マップ内の各長さの文字列を1つだけキーとして使用できます。

Ideoneのコード


12

はい、の3番目のテンプレートパラメータmapは、バイナリ述語であるコンパレータを指定します。例:

struct ByLength : public std::binary_function<string, string, bool>
{
    bool operator()(const string& lhs, const string& rhs) const
    {
        return lhs.length() < rhs.length();
    }
};

int main()
{
    typedef map<string, string, ByLength> lenmap;
    lenmap mymap;

    mymap["one"] = "one";
    mymap["a"] = "a";
    mymap["fewbahr"] = "foobar";

    for( lenmap::const_iterator it = mymap.begin(), end = mymap.end(); it != end; ++it )
        cout << it->first << "\n";
}

11
なぜ派生するのstd::binary_functionですか?必要ですか?
Devolus 2016

12
std::binary_functionはc ++ 17で削除されているため、この回答ではおそらく更新を使用できます。
ダン・オルソン

1

比較関数へのポインターのタイプをマップへの3番目のタイプとして指定し、マップコンストラクターへの関数ポインターを提供します。
map<keyType, valueType, typeOfPointerToFunction> mapName(pointerToComparisonFunction);

以下の例を見て、イテレータをキーおよび値としてmap、に比較関数を提供してください。vectorint

#include "headers.h"

bool int_vector_iter_comp(const vector<int>::iterator iter1, const vector<int>::iterator iter2) {
    return *iter1 < *iter2;
}

int main() {
    // Without providing custom comparison function
    map<vector<int>::iterator, int> default_comparison;

    // Providing custom comparison function
    // Basic version
    map<vector<int>::iterator, int,
        bool (*)(const vector<int>::iterator iter1, const vector<int>::iterator iter2)>
        basic(int_vector_iter_comp);

    // use decltype
    map<vector<int>::iterator, int, decltype(int_vector_iter_comp)*> with_decltype(&int_vector_iter_comp);

    // Use type alias or using
    typedef bool my_predicate(const vector<int>::iterator iter1, const vector<int>::iterator iter2);
    map<vector<int>::iterator, int, my_predicate*> with_typedef(&int_vector_iter_comp);

    using my_predicate_pointer_type = bool (*)(const vector<int>::iterator iter1, const vector<int>::iterator iter2);
    map<vector<int>::iterator, int, my_predicate_pointer_type> with_using(&int_vector_iter_comp);


    // Testing 
    vector<int> v = {1, 2, 3};

    default_comparison.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    default_comparison.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    default_comparison.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    default_comparison.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << default_comparison.size() << endl;
    for (auto& p : default_comparison) {
        cout << *(p.first) << ": " << p.second << endl;
    }

    basic.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    basic.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    basic.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    basic.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << basic.size() << endl;
    for (auto& p : basic) {
        cout << *(p.first) << ": " << p.second << endl;
    }

    with_decltype.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    with_decltype.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    with_decltype.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    with_decltype.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << with_decltype.size() << endl;
    for (auto& p : with_decltype) {
        cout << *(p.first) << ": " << p.second << endl;
    }

    with_typedef.insert(pair<vector<int>::iterator, int>({v.end(), 0}));
    with_typedef.insert(pair<vector<int>::iterator, int>({v.begin(), 0}));
    with_typedef.insert(pair<vector<int>::iterator, int>({v.begin(), 1}));
    with_typedef.insert(pair<vector<int>::iterator, int>({v.begin() + 1, 1}));

    cout << "size: " << with_typedef.size() << endl;
    for (auto& p : with_typedef) {
        cout << *(p.first) << ": " << p.second << endl;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.