Як я можу створити власний компаратор для карти?


84
typedef map<string, string> myMap;

Коли вставляється нова пара до myMap, він буде використовувати ключ stringдля порівняння за допомогою власного компаратора рядків. Чи можна замінити цей компаратор? Наприклад, я хотів би порівняти ключ stringза довжиною, а не за алфавітом. Або існує інший спосіб сортування карти?

Відповіді:


141

std::mapприймає до чотирьох аргументів типу шаблону, третій - компаратор. Наприклад:

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конструктору s .

Однак зауважте, що при порівнянні за довжиною на карті ви можете мати лише один рядок кожної довжини як ключ.


4
зауважимо, що ми можемо використовувати мультимап, якщо хочемо містити дубльований ключ
Xitrum

@GeorgFritzsche, будь-який шанс, що ви надаєте приклад передачі порівняльника до конструктора?
bpeikes

1
@bpeikes: Це не виглядає надто інакше:std::map<std::string, std::string> myMap(cmpByStringLength());
Георг Фріцше

У мене була проблема зі std :: map <int, int>, деякі зі збільшенням порядку, а інші за зменшенням порядку. Я не хотів використовувати std :: map <int, int, std :: larger> та std :: map <int, int, std :: less>, оскільки тоді я не міг використовувати карти, відсортовані в різних порядках як параметри для однієї функції, якщо я не зробив все шаблоном. Я виявив, що мені потрібно було зробити наступне: typedef std :: map <int, int, (bool) * (int, int)> mymap; Тоді я зміг передати функції. Я спробував наступне, але це не спрацювало: typedef std :: map <int, int> mymap; mymap map1 (std :: less); карта mymap2 (std :: більша);
bpeikes

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

20

Оскільки 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
два
три
фууура

Я хотів би повторити останню ноту відповіді Георга: При порівнянні за довжиною ви можете мати лише один рядок кожної довжини на карті як ключ.

Код на Ideone


12

Так, третій параметр шаблону на 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

12
std::binary_functionвидаляється в c ++ 17, тому ця відповідь могла б використати оновлення.
Ден Олсон,

1

Вкажіть тип вказівника на вашу функцію порівняння як 3-й тип на карті та надайте вказівник на конструктор карти:
map<keyType, valueType, typeOfPointerToFunction> mapName(pointerToComparisonFunction);

Погляньте на приклад нижче для надання функції порівняння для a map, з vectorітератором як ключем і intяк значення.

#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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.