158 votes

Comment trier un vecteur de paires en fonction du deuxième élément de la paire ?

Si j'ai un vecteur de paires :

std::vector<std::pair<int, int> > vec;

Existe-t-il un moyen simple de trier la liste en augmentation de ordre basé sur le deuxième élément de la paire ?

Je sais que je peux écrire un petit objet fonction qui fera le travail, mais existe-t-il un moyen d'utiliser des parties existantes de la fonction STL et std::less pour faire le travail directement ?

EDIT : Je comprends que je peux écrire une fonction ou une classe séparée à passer au troisième argument pour trier. La question est de savoir si je peux ou non la construire à partir d'une fonction standard. Je voudrais vraiment quelque chose qui ressemble à :

std::sort(vec.begin(), vec.end(), std::something_magic<int, int, std::less>());

0 votes

Voici un exemple :<br> std::sort dans un vecteur de paires

1 votes

c++ n'a pas de lamdas donc vous ne pouvez pas faire exactement ce que vous voulez, vous devrez créer une fonction/functeur séparé. Cela peut se faire en une seule fois, donc cela ne devrait pas être un gros problème.

2 votes

Le C++ a des lambdas maintenant ! Woo !

269voto

Evan Teran Points 42370

EDIT : en utilisant c++14, la meilleure solution est très facile à écrire grâce aux lambdas qui peuvent maintenant avoir des paramètres de type auto . C'est ma solution préférée du moment

std::sort(v.begin(), v.end(), [](auto &left, auto &right) {
    return left.second < right.second;
});

PREMIÈRE RÉPONSE :

Il suffit d'utiliser un comparateur personnalisé (il s'agit d'un troisième argument facultatif de la commande std::sort )

struct sort_pred {
    bool operator()(const std::pair<int,int> &left, const std::pair<int,int> &right) {
        return left.second < right.second;
    }
};

std::sort(v.begin(), v.end(), sort_pred());

Si vous utilisez un compilateur C++11, vous pouvez écrire la même chose en utilisant des lambdas :

std::sort(v.begin(), v.end(), [](const std::pair<int,int> &left, const std::pair<int,int> &right) {
    return left.second < right.second;
});

EDIT En réponse aux modifications apportées à votre question, voici quelques réflexions ... si vous vraiment Pour être créatif et pouvoir réutiliser souvent ce concept, il suffit de créer un modèle :

template <class T1, class T2, class Pred = std::less<T2> >
struct sort_pair_second {
    bool operator()(const std::pair<T1,T2>&left, const std::pair<T1,T2>&right) {
        Pred p;
        return p(left.second, right.second);
    }
};

alors vous pouvez le faire aussi :

std::sort(v.begin(), v.end(), sort_pair_second<int, int>());

ou même

std::sort(v.begin(), v.end(), sort_pair_second<int, int, std::greater<int> >());

Mais pour être honnête, tout cela est un peu exagéré, il suffit d'écrire la fonction de 3 lignes et d'en finir avec elle :-P

1 votes

Gardez à l'esprit que cela est différent de operator< sur pair<T1,T2> . Le comparateur par défaut utilise les deux premier et deuxième élément (au cas où les premiers sont égaux). Ici, seul le deuxième élément est utilisé.

0 votes

@Googol : C'est précisément ce que le PO a demandé... Il a dit : "is there and easy way to sort the list in increasing order based on the second element of the pair?"

0 votes

@evan-teran, oui, je sais. J'indiquais seulement que si les deux seconds éléments sont égaux, alors le résultat peut être confus (s'il est utilisé pour un tri, par exemple). Le comparateur par défaut ne souffre pas de ce problème, car il utilise le deuxième élément pour départager. J'ai répondu à cette question en cherchant un comparateur qui utilise le deuxième élément comme information principale pour la comparaison, mais j'avais également besoin qu'il utilise le premier élément pour départager les ex-aequo, et j'aimerais donc éviter que d'autres ne passent à côté de ce point (comme je l'ai fait, en fait).

71voto

Johannes Schaub - litb Points 256113

Vous pouvez utiliser Boost comme ceci :

std::sort(a.begin(), a.end(), 
          boost::bind(&std::pair<int, int>::second, _1) <
          boost::bind(&std::pair<int, int>::second, _2));

Je ne connais pas de méthode standard pour faire cela de manière aussi courte et concise, mais vous pouvez saisir boost::bind tout est constitué d'en-têtes.

1 votes

+1 pour l'utilisation de Boost. En fait, avec un compilateur moderne, vous pourriez probablement déjà remplacer Boost par std::tr1, car cela fera bientôt partie de la norme.

0 votes

malheureusement, j'ai essayé la même chose avec le std::bind c++1x de gcc trunk, et cela a échoué parce qu'il n'a pas l'op< pour bind. je ne sais pas cependant si ce que c++1x dit à ce sujet. probablement il vous dit d'utiliser lambda pour cela :)

1 votes

Je suppose que le boost n'est pas standard, mais c'est assez proche :-)

30voto

Andreas Spindler Points 1612

Avec C++0x, nous pouvons utiliser des fonctions lambda :

using namespace std;
vector<pair<int, int>> v;
        .
        .
sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) {
             return lhs.second < rhs.second; } );

Dans cet exemple, le type de retour bool est implicitement déduit.

Types de retour lambda

Lorsqu'une fonction lambda ne comporte qu'une seule instruction, et que celle-ci est une instruction de retour, le compilateur peut déduire le type de retour. Tiré de C++11, §5.1.2/4 :

...

  • Si l'énoncé composé est de la forme { return expression ; } le type de l'expression retournée après conversion de lvalue en rvalue (4.1), conversion de tableau en pointeur (4.2), et conversion de fonction en pointeur (4.3) ;
  • autrement, void .

Pour spécifier explicitement le type de retour, utilisez la forme []() -> Type { } comme dans :

sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) -> bool {
             if (lhs.second == 0)
                 return true;
             return lhs.second < rhs.second; } );

1 votes

Pourquoi if (lhs.second == 0) ?

0 votes

Aucune signification particulière ; lhs.second < rhs.second peut revenir true ou false et le compilateur peut clairement déduire bool . Je voulais juste démontrer que le []() -> Type { } cas.

0 votes

Au moins avec clang, cette déduction implicite peut ne pas fonctionner correctement, j'ai dû ajouter ->bool comme type de retour lambda pour que cela fonctionne correctement.

5voto

Leon Timmermans Points 23230

Pour quelque chose de réutilisable :

template<template <typename> class P = std::less >
struct compare_pair_second {
    template<class T1, class T2> bool operator()(const std::pair<T1, T2>& left, const std::pair<T1, T2>& right) {
        return P<T2>()(left.second, right.second);
    }
};

Vous pouvez l'utiliser comme

std::sort(foo.begin(), foo.end(), compare_pair_second<>());

ou

std::sort(foo.begin(), foo.end(), compare_pair_second<std::less>());

1voto

Jason Lepack Points 2755

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