キーではなく値でstd::map
をソートする必要があります。それを行う簡単な方法はありますか?
私は次のスレッドから1つの解決策を得ました:
std :: map sort by data?
より良い解決策はありますか?
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?
正解はすでに投稿されていますが、これをきれいに行う方法のデモを追加すると思いました。
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!
}
汎用連想ソース(C++ 11が必要)
ソース連想コンテナにstd::map
の代替(std::unordered_map
など)を使用している場合、別個のオーバーロードをコーディングできますが、最終的にアクションは同じであるため、一般化された連想可変長テンプレートを使用するコンテナは、eitherマッピング構成に使用できます。
// 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;
}
これは、フリップのソースとしてstd::map
とstd::unordered_map
の両方で機能します。
似たようなものが必要でしたが、反転したマップは機能しません。私は自分のマップ(下のfreq)をペアのベクトルにコピーし、必要に応じてペアをソートしました。
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;
}
);
マップ内の値をソート順に表示する場合は、マップから値をベクターにコピーしてベクターをソートします。
私はOliからの答え(マップを反転する)が好きですが、問題があるようです:コンテナマップは同じキーを持つ2つの要素を許可しません。
解決策は、dstをマルチマップタイプにすることです。もう1つは、srcをベクターにダンプし、ベクターをソートすることです。前者はOliの答えに若干の修正を必要とし、後者はSTLコピーを使用して簡潔に実装できます
#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;
};
マルチマップを使用してOliのソリューション( https://stackoverflow.com/a/5056797/2472351 )を構築するには、使用した2つのテンプレート関数を次のように置き換えることができます。
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;
}
以下は、フリップを実行した後に保存されるすべてのキーと値のペアを表示するプログラムの例です。
#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;
}
結果:
std::map
をこの方法で並べ替えることはできません。マップ内のエントリがキーで並べ替えられるためです。値で並べ替える場合は、キーと値を交換した新しいstd::map
を作成する必要があります。
map<long, double> testMap;
map<double, long> testMap2;
// Insert values from testMap to testMap2
// The values in testMap2 are sorted by the double value
ダブルキーはtestMap2
で一意であるか、std::multimap
を使用する必要があることに注意してください。
次のサンプルコードでは、キーが文字列(Word)で値がunsigned int(Wordオカレンス)であるWord_mapマップの上位ワードを出力する簡単な方法を記述しました。
アイデアはシンプルで、現在の上位のWordを見つけてマップから削除します。最適化されていませんが、マップが大きくなく、マップ全体を並べ替えるのではなく、上位Nワードのみを出力する必要がある場合にうまく機能します。
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;
}
別の解決策は、std :: make_move_iteratorを使用して新しいベクトルを構築することです(C++ 11)
int main(){
std::map<std::string, int> map;
//Populate map
std::vector<std::pair<std::string, int>> v {std::make_move_iterator(begin(map)),
std::make_move_iterator(end(map))};
// Create a vector with the map parameters
sort(begin(v), end(v),
[](auto p1, auto p2){return p1.second > p2.second;});
// Using sort + lambda function to return an ordered vector
// in respect to the int value that is now the 2nd parameter
// of our newly created vector v
}
std::map
その値でソートされたものは本質的にstd::set
。はるかに簡単な方法は、マップ内のすべてのエントリをセットにコピーすることです( here から取得および適用)
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 );
}
}
注意点:マップに同じ値を持つ異なるキーが含まれている場合、それらはセットに挿入されず、失われません。
Uはboost :: bimapを使用することを検討できます。これにより、マップはキーと値で同時にソートされているように感じられるかもしれません(ただし、実際にはそうではありません)
反転された構造は、マップではなくマルチマップになる可能性があります。したがって、上記のflip_mapの例では、Bのすべての要素が結果のデータ構造に表示されるとは限りません。
このコンテキストでは、マップをマルチマップに変換する必要があります。元のマップに重複する値が多数ある場合、多くの情報が失われるため、マップをセットに変換するのは良くないと思います。ここに私の解決策があります。値でソートする小なりコンパレータを定義しました(cmp関数)。要求に応じてcmp関数をカスタマイズできます。
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));