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?
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.
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;
}
);
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.
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;
};
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:
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
.
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;
}
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.
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.
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));
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)