web-dev-qa-db-fra.com

Tri de std :: map avec value

Je dois trier un std::map par valeur plutôt que par clé. Y a-t-il un moyen facile de le faire?

J'ai eu une solution du fil suivant:
std :: map trier par données?
Y a-t-il une meilleure solution? 

map<long, double> testMap;
// some code to generate the values in the map.

sort(testMap.begin(), testMap.end());  // is there any function like this to sort the map?
48
user619237

Même si les réponses correctes ont déjà été postées, j'ai pensé ajouter une démonstration de la façon dont vous pouvez le faire proprement:

template<typename A, typename B>
std::pair<B,A> flip_pair(const std::pair<A,B> &p)
{
    return std::pair<B,A>(p.second, p.first);
}

template<typename A, typename B>
std::multimap<B,A> flip_map(const std::map<A,B> &src)
{
    std::multimap<B,A> dst;
    std::transform(src.begin(), src.end(), std::inserter(dst, dst.begin()), 
                   flip_pair<A,B>);
    return dst;
}

int main(void)
{
    std::map<int, double> src;

    ...    

    std::multimap<double, int> dst = flip_map(src);
    // dst is now sorted by what used to be the value in src!
}

Source associative générique (requiert C++ 11)

Si vous utilisez une alternative à std::map pour le conteneur associatif source (tel que std::unordered_map), vous pouvez coder une surcharge distincte, mais au final l'action est toujours identique. soit construction de mappage:

// flips an associative container of A,B pairs to B,A pairs
template<typename A, typename B, template<class,class,class...> class M, class... Args>
std::multimap<B,A> flip_map(const M<A,B,Args...> &src)
{
    std::multimap<B,A> dst;
    std::transform(src.begin(), src.end(),
                   std::inserter(dst, dst.begin()),
                   flip_pair<A,B>);
    return dst;
}

Cela fonctionnera pour std::map et std::unordered_map en tant que source du retournement.

50
Oliver Charlesworth

J'avais besoin de quelque chose de similaire, mais la carte retournée ne fonctionnerait pas pour moi. Je viens de copier ma carte (freq ci-dessous) dans un vecteur de paires, puis de trier les paires comme je le voulais.

std::vector<std::pair<int, int>> pairs;
for (auto itr = freq.begin(); itr != freq.end(); ++itr)
    pairs.Push_back(*itr);

sort(pairs.begin(), pairs.end(), [=](std::pair<int, int>& a, std::pair<int, int>& b)
{
    return a.second < b.second;
}
);
30
NielW

Si vous souhaitez présenter les valeurs d'une carte dans un ordre trié, copiez les valeurs de la carte dans un vecteur et triez le vecteur. 

11
Bogatyr

J'aime la réponse d'Oli (retourner une carte), mais il semble y avoir un problème: la carte conteneur ne permet pas deux éléments avec la même clé.

Une solution consiste à faire dst le type multimap. Une autre consiste à déposer src dans un vecteur et à trier le vecteur. Le premier nécessite des modifications mineures dans la réponse d'Oli, et le dernier peut être mis en œuvre avec une copie STL de manière concise

#include <iostream>
#include <utility>
#include <map>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
  map<int, int> m;
  m[11] = 1;
  m[22] = 2;
  m[33] = 3;

  vector<pair<int, int> > v;
  copy(m.begin(),
       m.end(),
       back_inserter<vector<pair<int, int> > >(v));

  for (size_t i = 0; i < v.size(); ++i) {
    cout << v[i].first << " , " << v[i].second << "\n";
  }

  return 0;
};
8
cxwangyi

Pour utiliser la solution Oli ( https://stackoverflow.com/a/5056797/2472351 ) à l'aide de plusieurs cartes, vous pouvez remplacer les deux fonctions de modèle qu'il avait utilisées par les suivantes:

template <typename A, typename B>
multimap<B, A> flip_map(map<A,B> & src) {

    multimap<B,A> dst;

    for(map<A, B>::const_iterator it = src.begin(); it != src.end(); ++it)
        dst.insert(pair<B, A>(it -> second, it -> first));

    return dst;
}

Voici un exemple de programme qui montre toutes les paires clé-valeur en cours de conservation après le retournement.

#include <iostream>
#include <map>
#include <string>
#include <algorithm>

using namespace std;

template <typename A, typename B>
multimap<B, A> flip_map(map<A,B> & src) {

    multimap<B,A> dst;

    for(typename map<A, B>::const_iterator it = src.begin(); it != src.end(); ++it)
        dst.insert(pair<B, A>(it -> second, it -> first));

    return dst;
}

int main() {

    map<string, int> test;
    test["Word"] = 1;
    test["spark"] = 15;
    test["the"] = 2;
    test["mail"] = 3;
    test["info"] = 3;
    test["sandwich"] = 15;

    cout << "Contents of original map:\n" << endl;
    for(map<string, int>::const_iterator it = test.begin(); it != test.end(); ++it)
        cout << it -> first << " " << it -> second << endl; 

    multimap<int, string> reverseTest = flip_map(test);

    cout << "\nContents of flipped map in descending order:\n" << endl;
    for(multimap<int, string>::const_reverse_iterator it = reverseTest.rbegin(); it != reverseTest.rend(); ++it)
        cout << it -> first << " " << it -> second << endl; 

    cout << endl;
}

Résultat:

enter image description here

3
ericgrosse

Vous ne pouvez pas trier un std::map de cette façon, car les entrées de la carte sont triées par la clé. Si vous souhaitez effectuer un tri par valeur, vous devez créer un nouveau std::map avec une clé et une valeur permutées.

map<long, double> testMap;
map<double, long> testMap2;

// Insert values from testMap to testMap2
// The values in testMap2 are sorted by the double value

Rappelez-vous que les doubles clés doivent être uniques dans testMap2 ou utilisez std::multimap.

3
Fox32

Dans l'exemple de code suivant, j'ai écrit un moyen simple de générer les mots clés dans une mappe Word_map où clé est chaîne (Word) et valeur, unsigned int (occurrence Word).

L'idée est simple: trouvez le dernier mot actuel et supprimez-le de la carte. Ce n'est pas optimisé, mais cela fonctionne bien lorsque la carte n'est pas grande et qu'il suffit de sortir les N mots du haut, au lieu de trier la carte entière.

const int NUMBER_OF_TOP_WORDS = 300;
for (int i = 1; i <= NUMBER_OF_TOP_WORDS; i++) {
  if (Word_map.empty())
    break;
  // Go through the map and find the max item.
  int max_value = 0;
  string max_Word = "";
  for (const auto& kv : Word_map) {
    if (kv.second > max_value) {
      max_value = kv.second;
      max_Word = kv.first;
    }
  }
  // Erase this entry and print.
  Word_map.erase(max_Word);
  cout << "Top:" << i << " Count:" << max_value << " Word:<" << max_Word << ">" <<     endl;
}
1
Jim Huang

Un std::map trié selon sa valeur est essentiellement un std::set. De loin, le moyen le plus simple est de copier toutes les entrées de la carte dans un jeu (pris et adapté de ici )

template <typename M, typename S> 
void MapToSet( const  M & m, S & s )
{
    typename M::const_iterator end = m.end();
    for( typename M::const_iterator it = m.begin(); it != end ; ++it )
    {
        s.insert( it->second );
    }
}

Une mise en garde: si la carte contient différentes clés avec la même valeur, elles ne seront pas insérées dans le jeu et seront perdues.

1
rubenvb

La structure retournée peut ne plus être une carte mais une carte multiple. Ainsi, dans l'exemple flip_map ci-dessus, tous les éléments de B n'apparaîtront pas nécessairement dans la structure de données résultante. 

1
Yuri Feldman

Dans ce contexte, nous devrions convertir la carte en carte multiple. Je pense que convertir la carte en un ensemble n’est pas une bonne chose car nous perdrons beaucoup d’informations au cas où il y aurait beaucoup de doublons dans la carte originale. Voici ma solution, j'ai défini le comparateur inférieur à celui triant par valeur (fonction cmp). Nous pouvons personnaliser la fonction cmp selon nos besoins. 

std::map<int, double> testMap = { {1,9.1}, {2, 8.0}, {3, 7.0}, {4,10.5} };
auto cmp = [](const double &lhs,
              const double &rhs)->bool
{
    return lhs < rhs;
};
std::multimap<double, int, decltype(cmp)> mmap(cmp);
for (auto item : testMap)
    mmap.insert(make_pair(item.second, item.first));
0
Loc Tran

Vous pouvez envisager d’utiliser boost :: bimap pour vous donner l’impression que la carte est triée simultanément par clé et par valeur (ce n’est pas ce qui se passe réellement)

0
user470988