106 votes

Comment puis-je créer mon propre comparateur pour une carte ?

typedef map<string, string> myMap;

Lors de l'insertion d'une nouvelle paire dans myMap il utilisera la clé string à comparer par son propre comparateur de chaînes. Est-il possible de remplacer ce comparateur ? Par exemple, j'aimerais comparer la clé string par sa longueur, pas par l'alphabet. Ou y a-t-il un autre moyen de trier la carte ?

174voto

Georg Fritzsche Points 59185

std::map prend jusqu'à quatre arguments de type modèle, le troisième étant un comparateur. Par exemple :

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;

Alternativement, vous pouvez aussi passer un comparateur à map s constructor .

Notez cependant que lors de la comparaison par longueur, vous ne pouvez avoir qu'une seule chaîne de chaque longueur dans la carte comme clé.

6 votes

Notez que nous pouvons utiliser multimap si nous voulons contenir les clés dupliquées

0 votes

@GeorgFritzsche une chance que vous fournissez un exemple de passer le comparateur au constructeur ?

1 votes

@bpeikes : Ça n'a pas l'air trop différent : std::map<std::string, std::string> myMap(cmpByStringLength());

34voto

honk Points 4740

Depuis C++11 vous pouvez également utiliser un expression lambda au lieu de définir une structure de comparaison :

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;

Sortie :

1
deux
trois
folie

Je voudrais répéter la dernière note de la réponse de Georg : Lorsque vous comparez par longueur, vous ne pouvez avoir qu'une seule chaîne de chaque longueur dans la carte comme clé.

Code sur Ideone

14voto

John Dibling Points 56814

Oui, le 3ème paramètre du modèle sur map spécifie le comparateur, qui est un prédicat binaire. Exemple :

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 votes

Pourquoi la dérive de std::binary_function ? Est-il nécessaire ?

12 votes

std::binary_function est supprimée dans c++17 donc cette réponse pourrait probablement être mise à jour.

3voto

yyFred Points 192

Spécifiez le type du pointeur vers votre fonction de comparaison comme troisième type dans la carte, et fournissez le pointeur de la fonction au constructeur de la carte :
map<keyType, valueType, typeOfPointerToFunction> mapName(pointerToComparisonFunction);

Examinez l'exemple ci-dessous pour fournir une fonction de comparaison à un fichier map avec vector comme clé et int comme valeur.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X