Flip map key-value pair - c++

I have a map. I want to flip the key-value so that it not becomes map. So basically the value of the first map becomes the key of the second map. How do i do this?
Example map:
1 - 1.0
2 - 2.0
After flip
1.0 - 1
2.0 - 2

The most straightforward way (that I know of) is to create a new map with the types flipped, and iterate the old one and add each key-value pair in reverse.
For example,
map<int, float> if_map;
// insert some items into if_map
if_map[1] = 43.11;
if_map[44] = -13421.438;
map<float, int> reversed;
for (map<int, float>::iterator i = if_map.begin(); i != if_map.end(); ++i)
reversed[i->second] = i->first;

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
template<typename A, typename B>
pair<B,A> flip_pair(const pair<A,B> &p)
{
return pair<B,A>(p.second, p.first);
}
template<typename A, typename B>
map<B,A> flip_map(const map<A,B> &src)
{
map<B,A> dst;
transform(src.begin(), src.end(), inserter(dst, dst.begin()),
flip_pair<A,B>);
return dst;
}
int main(void)
{
std::map<char, int> src;
src['a'] = 10;
src['b'] = 20;
src['c'] = 160;
src['d'] = 110;
src['e'] = 0;
std::map<int, char> dst = flip_map(src);
map<int, char>::iterator it;
for(it=dst.begin(); it!=dst.end(); it++) {
cout << it->first << " : " << it->second << endl;
}
}

for (auto i=normal.begin(); i!=normal.end(); ++i)
flipped[i->second] = i->first;

If you want lookup in both directions then you can use Boost.bimap

Related

Is there any way to create a value sorted map? [duplicate]

I need to sort an std::map by value rather than by key. Is there an easy way to do it?
I got one solution from the follwing thread:
std::map sort by data?
Is there a better 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?
Even though correct answers have already been posted, I thought I'd add a demo of how you can do this cleanly:
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!
}
Generic Associative Source (requires C++11)
If you're using an alternate to std::map for the source associative container (such as std::unordered_map), you could code a separate overload, but in the end the action is still the same, so a generalized associative container using variadic templates can be used for either mapping construct:
// 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;
}
This will work for both std::map and std::unordered_map as the source of the flip.
I needed something similar, but the flipped map wouldn't work for me. I just copied out my map (freq below) into a vector of pairs, then sorted the pairs however I wanted.
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;
}
);
If you want to present the values in a map in sorted order, then copy the values from the map to vector and sort the vector.
I like the the answer from Oli (flipping a map), but seems it has a problem: the container map does not allow two elements with the same key.
A solution is to make dst the type multimap. Another one is to dump src into a vector and sort the vector. The former requires minor modifications to Oli's answer, and the latter can be implemented using STL copy concisely
#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;
};
To build on Oli's solution (https://stackoverflow.com/a/5056797/2472351) using multimaps, you can replace the two template functions he used with the following:
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;
}
Here is an example program that shows all the key-value pairs being preserved after performing the flip.
#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;
}
Result:
You can't sort a std::map this way, because a the entries in the map are sorted by the key. If you want to sort by value, you need to create a new std::map with swapped key and value.
map<long, double> testMap;
map<double, long> testMap2;
// Insert values from testMap to testMap2
// The values in testMap2 are sorted by the double value
Remember that the double keys need to be unique in testMap2 or use std::multimap.
A std::map sorted by it's value is in essence a std::set. By far the easiest way is to copy all entries in the map to a set (taken and adapted from 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 );
}
}
One caveat: if the map contains different keys with the same value, they will not be inserted into the set and be lost.
Flipped structure might no longer be a map but rather a multimap, thus in the flip_map example above not all elements from B will necessarily appear in the resulting data structure.
Another solution would be the usage of std::make_move_iterator to build a new vector (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
}
U can consider using boost::bimap that might gave you a feeling that map is sorted by key and by values simultaneously (this is not what really happens, though)
In the following sample code, I wrote an simple way to output top words in an word_map map where key is string (word) and value is unsigned int (word occurrence).
The idea is simple, find the current top word and delete it from the map. It's not optimized, but it works well when the map is not large and we only need to output the top N words, instead of sorting the whole map.
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;
}
An alternative way to sorting a std::map without any additional copying or transformation is to redefine the std::map with different Compare type:
namespace nonstd {
template <class Key,
class T,
class Compare = std::greater<T>,
class Allocator = std::allocator<std::pair<Key const, T>>
>
using map = std::map<Key, T, Compare, Allocator>;
}
int main() {
nonstd::map<char, std::size_t> const values = {
{'A', 3}, {'B', 2}, {'C', 5}
};
for (auto const& value : values) {
std::clog << value.first << " : " << value.second << std::endl;
}
}
In this context, we should convert map to multimap. I think convert map to set is not good because we will lose many information in case of there is many duplicate values in the original map. Here is my solution, I defined the less than comparator that sort by value (cmp function). We can customize the cmp function as our demand.
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));
Just put the values into the vector and sort the vector on the value of each map key.
#include <bits/stdc++.h>
using namespace std;
int main()
{
std::map<std::string, int> mymap;
mymap.insert(std::make_pair("earth", 5));
mymap.insert(std::make_pair("moon", 33));
mymap.insert(std::make_pair("sun", 2));
vector<std::pair<std::string, int>> myvector {mymap.begin(), mymap.end()};
sort(myvector.begin(), myvector.end(), [](std::pair<std::string, int> l, std::pair<std::string, int> r)
{
return l.second < r.second;
});
return 0;
}

Sorting a map by it's element in value which is pair using C++ [duplicate]

I need to sort an std::map by value rather than by key. Is there an easy way to do it?
I got one solution from the follwing thread:
std::map sort by data?
Is there a better 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?
Even though correct answers have already been posted, I thought I'd add a demo of how you can do this cleanly:
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!
}
Generic Associative Source (requires C++11)
If you're using an alternate to std::map for the source associative container (such as std::unordered_map), you could code a separate overload, but in the end the action is still the same, so a generalized associative container using variadic templates can be used for either mapping construct:
// 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;
}
This will work for both std::map and std::unordered_map as the source of the flip.
I needed something similar, but the flipped map wouldn't work for me. I just copied out my map (freq below) into a vector of pairs, then sorted the pairs however I wanted.
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;
}
);
If you want to present the values in a map in sorted order, then copy the values from the map to vector and sort the vector.
I like the the answer from Oli (flipping a map), but seems it has a problem: the container map does not allow two elements with the same key.
A solution is to make dst the type multimap. Another one is to dump src into a vector and sort the vector. The former requires minor modifications to Oli's answer, and the latter can be implemented using STL copy concisely
#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;
};
To build on Oli's solution (https://stackoverflow.com/a/5056797/2472351) using multimaps, you can replace the two template functions he used with the following:
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;
}
Here is an example program that shows all the key-value pairs being preserved after performing the flip.
#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;
}
Result:
You can't sort a std::map this way, because a the entries in the map are sorted by the key. If you want to sort by value, you need to create a new std::map with swapped key and value.
map<long, double> testMap;
map<double, long> testMap2;
// Insert values from testMap to testMap2
// The values in testMap2 are sorted by the double value
Remember that the double keys need to be unique in testMap2 or use std::multimap.
A std::map sorted by it's value is in essence a std::set. By far the easiest way is to copy all entries in the map to a set (taken and adapted from 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 );
}
}
One caveat: if the map contains different keys with the same value, they will not be inserted into the set and be lost.
Flipped structure might no longer be a map but rather a multimap, thus in the flip_map example above not all elements from B will necessarily appear in the resulting data structure.
Another solution would be the usage of std::make_move_iterator to build a new vector (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
}
U can consider using boost::bimap that might gave you a feeling that map is sorted by key and by values simultaneously (this is not what really happens, though)
In the following sample code, I wrote an simple way to output top words in an word_map map where key is string (word) and value is unsigned int (word occurrence).
The idea is simple, find the current top word and delete it from the map. It's not optimized, but it works well when the map is not large and we only need to output the top N words, instead of sorting the whole map.
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;
}
An alternative way to sorting a std::map without any additional copying or transformation is to redefine the std::map with different Compare type:
namespace nonstd {
template <class Key,
class T,
class Compare = std::greater<T>,
class Allocator = std::allocator<std::pair<Key const, T>>
>
using map = std::map<Key, T, Compare, Allocator>;
}
int main() {
nonstd::map<char, std::size_t> const values = {
{'A', 3}, {'B', 2}, {'C', 5}
};
for (auto const& value : values) {
std::clog << value.first << " : " << value.second << std::endl;
}
}
In this context, we should convert map to multimap. I think convert map to set is not good because we will lose many information in case of there is many duplicate values in the original map. Here is my solution, I defined the less than comparator that sort by value (cmp function). We can customize the cmp function as our demand.
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));
Just put the values into the vector and sort the vector on the value of each map key.
#include <bits/stdc++.h>
using namespace std;
int main()
{
std::map<std::string, int> mymap;
mymap.insert(std::make_pair("earth", 5));
mymap.insert(std::make_pair("moon", 33));
mymap.insert(std::make_pair("sun", 2));
vector<std::pair<std::string, int>> myvector {mymap.begin(), mymap.end()};
sort(myvector.begin(), myvector.end(), [](std::pair<std::string, int> l, std::pair<std::string, int> r)
{
return l.second < r.second;
});
return 0;
}

Change or delete elements of vector

I have following problem. My vector contains pairs of pairs (see example below).
In the example below I will push_back vector with some "random" data.
What will be best solution to delete the vector element if any of their values will be equal i.e. 100 and update value if less than 100.
i.e.
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
MyMap pair1;
MyMap pair2;
In first example I want to update this pair because pair1.first is less than 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 101;
pair2.second = 101;
In second example I want to delete this pair because pair2.first is equal to 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 100;
pair2.second = 101;
Using functor "check" I am able to delete one or more elements (in this example just one).
It is possible to increase every value of that pair by 1 using std::replace_if function?
Is there any function that will update this value if any of these values will be lower then "X" and delete if any of these values will be equal "X"?
I know how to do it writing my own function but I am curious.
#include "stdafx.h"
#include<algorithm>
#include<vector>
#include<iostream>
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
void PrintAll(std::vector<MyPair> & v);
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size);
class check
{
public:
check(int c)
: cmpValue(c)
{
}
bool operator()(const MyPair & mp) const
{
return (mp.first.first == cmpValue);
}
private:
int cmpValue;
};
int _tmain(int argc, _TCHAR* argv[])
{
const int size = 10;
std::vector<MyPair> vecotorOfMaps;
FillVectorWithSomeStuff(vecotorOfMaps, size);
PrintAll(vecotorOfMaps);
std::vector<MyPair>::iterator it = std::find_if(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
if (it != vecotorOfMaps.end()) vecotorOfMaps.erase(it);
PrintAll(vecotorOfMaps);
system("pause");
return 0;
}
std::ostream & operator<<(std::ostream & stream, const MyPair & mp)
{
stream << "First:First = " << mp.first.first << " First.Second = " << mp.first.second << std::endl;
stream << "Second:First = " << mp.second.first << " Second.Second = " << mp.second.second << std::endl;
stream << std::endl;
return stream;
}
void PrintAll(std::vector<MyPair> & v)
{
for (std::vector<MyPair>::iterator it = v.begin(); it != v.end(); ++it)
{
std::cout << *it;
}
}
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size)
{
for (int i = 0; i < size; ++i)
{
MyMap m1(i + i * 10, i + i * 20);
MyMap m2(i + i * 30, i + i * 40);
MyPair mp(m1, m2);
v.push_back(mp);
}
}
Use std::stable_partition, along with std::for_each:
#include <algorithm>
//...partition the elements in the vector
std::vector<MyPair>::iterator it =
std::stable_partition(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
//erase the ones equal to "check"
vecotorOfMaps.erase(vecotorOfMaps.begin(), it);
// adjust the ones that were left over
for_each(vecotorOfMaps.begin(), vecotorOfMaps.end(), add(1));
Basically, the stable_partition places all the items you will delete in the front of the array (the left side of the partiton it), and all of the other items to the right of it.
Then all that is done is to erase the items on the left of it (since they're equal to 100), and once that's done, go through the resulting vector, adding 1 to eac

Sorting std::map using value

I need to sort an std::map by value rather than by key. Is there an easy way to do it?
I got one solution from the follwing thread:
std::map sort by data?
Is there a better 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?
Even though correct answers have already been posted, I thought I'd add a demo of how you can do this cleanly:
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!
}
Generic Associative Source (requires C++11)
If you're using an alternate to std::map for the source associative container (such as std::unordered_map), you could code a separate overload, but in the end the action is still the same, so a generalized associative container using variadic templates can be used for either mapping construct:
// 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;
}
This will work for both std::map and std::unordered_map as the source of the flip.
I needed something similar, but the flipped map wouldn't work for me. I just copied out my map (freq below) into a vector of pairs, then sorted the pairs however I wanted.
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;
}
);
If you want to present the values in a map in sorted order, then copy the values from the map to vector and sort the vector.
I like the the answer from Oli (flipping a map), but seems it has a problem: the container map does not allow two elements with the same key.
A solution is to make dst the type multimap. Another one is to dump src into a vector and sort the vector. The former requires minor modifications to Oli's answer, and the latter can be implemented using STL copy concisely
#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;
};
To build on Oli's solution (https://stackoverflow.com/a/5056797/2472351) using multimaps, you can replace the two template functions he used with the following:
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;
}
Here is an example program that shows all the key-value pairs being preserved after performing the flip.
#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;
}
Result:
You can't sort a std::map this way, because a the entries in the map are sorted by the key. If you want to sort by value, you need to create a new std::map with swapped key and value.
map<long, double> testMap;
map<double, long> testMap2;
// Insert values from testMap to testMap2
// The values in testMap2 are sorted by the double value
Remember that the double keys need to be unique in testMap2 or use std::multimap.
A std::map sorted by it's value is in essence a std::set. By far the easiest way is to copy all entries in the map to a set (taken and adapted from 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 );
}
}
One caveat: if the map contains different keys with the same value, they will not be inserted into the set and be lost.
Flipped structure might no longer be a map but rather a multimap, thus in the flip_map example above not all elements from B will necessarily appear in the resulting data structure.
Another solution would be the usage of std::make_move_iterator to build a new vector (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
}
U can consider using boost::bimap that might gave you a feeling that map is sorted by key and by values simultaneously (this is not what really happens, though)
In the following sample code, I wrote an simple way to output top words in an word_map map where key is string (word) and value is unsigned int (word occurrence).
The idea is simple, find the current top word and delete it from the map. It's not optimized, but it works well when the map is not large and we only need to output the top N words, instead of sorting the whole map.
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;
}
An alternative way to sorting a std::map without any additional copying or transformation is to redefine the std::map with different Compare type:
namespace nonstd {
template <class Key,
class T,
class Compare = std::greater<T>,
class Allocator = std::allocator<std::pair<Key const, T>>
>
using map = std::map<Key, T, Compare, Allocator>;
}
int main() {
nonstd::map<char, std::size_t> const values = {
{'A', 3}, {'B', 2}, {'C', 5}
};
for (auto const& value : values) {
std::clog << value.first << " : " << value.second << std::endl;
}
}
In this context, we should convert map to multimap. I think convert map to set is not good because we will lose many information in case of there is many duplicate values in the original map. Here is my solution, I defined the less than comparator that sort by value (cmp function). We can customize the cmp function as our demand.
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));
Just put the values into the vector and sort the vector on the value of each map key.
#include <bits/stdc++.h>
using namespace std;
int main()
{
std::map<std::string, int> mymap;
mymap.insert(std::make_pair("earth", 5));
mymap.insert(std::make_pair("moon", 33));
mymap.insert(std::make_pair("sun", 2));
vector<std::pair<std::string, int>> myvector {mymap.begin(), mymap.end()};
sort(myvector.begin(), myvector.end(), [](std::pair<std::string, int> l, std::pair<std::string, int> r)
{
return l.second < r.second;
});
return 0;
}

Sorting a std::map by value before output & destroy

I'm aware that map is not prepared to be sorted. It's heavily optimized for fast and random key access and actually doesn't support std::sort.
My current problem is that I've a full map<std::string,int> which I'm not going to use anymore. I just need to extract 10 pairs in value(int) order and destroy it.
The best thing, if it was possible, would be to sort it in place and then iterate it 10 times, but that apparently is not a solution.
I'm trying different solutions as going through a multimap<int,string> (to allow duplicate keys), but I'd like to know if there is a more elegant solution, using stl algorithms as much as posible.
EDIT:
I'm using a map because for the 99% of the time, I need it as a map: fast key lookups to increase values. Just need a good way of later extracting in value order when I don't need the map anymore.
Current approach whould be:
std::copy the map(std::string,int) to a vector(pair(std::string,int))
sort the vector
get the first 10 values
destroy vector and map
Maps are stored as a tree sorted in key order. You want the 10 smallest (or largest) integer values, and their keys, right?
In that case, iterate the map and put all the key-value pairs in a vector of pairs (std::vector<std::pair<std::string, int> >). I think you can just use the two-iterator-arg constructor of std::vector for this. Then use std::partial_sort on the vector. Specify a comparator to partial_sort, which compares pairs by just comparing the value int, ignoring the key string. Then you have the 10 pairs you want at the start of the vector, and the rest of the vector contains the remaining pairs in an unspecified order.
Code (untested):
typedef std::pair<std::string, int> mypair;
struct IntCmp {
bool operator()(const mypair &lhs, const mypair &rhs) {
return lhs.second < rhs.second;
}
};
void print10(const std::map<std::string,int> &mymap) {
std::vector<mypair> myvec(mymap.begin(), mymap.end());
assert(myvec.size() >= 10);
std::partial_sort(myvec.begin(), myvec.begin() + 10, myvec.end(), IntCmp());
for (int i = 0; i < 10; ++i) {
std::cout << i << ": " << myvec[i].first
<< "-> " << myvec[i].second << "\n";
}
}
Note that if there are several strings with the same value, either side of the limit of 10, then it's not specified which ones you get. You can control this by having your comparator look at the string too, in cases where the integers are equal.
For iterating by value you could use boost::multi_index. It will looks as follows:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
using namespace boost::multi_index;
struct X {
X( std::string val_str, int val_int ) : val_str(val_str), val_int(val_int) {};
std::string val_str;
int val_int;
};
typedef multi_index_container<
X,
indexed_by<
hashed_unique< member<X, std::string, &X::val_str> >,
ordered_non_unique< member<X, int, &X::val_int> >
>
> X_map;
void func()
{
X_map data;
data.insert( X("test", 1) );
// ...
// search by val_str
// complexity is equal to O(1) for hashed index (worst cast O(n) ),
// and O(log n) for ordered index
X_map::const_iterator it = data.find( "test" );
// ...
// iterate in order of val_int
size_t N = 0;
for ( X_map::nth_index<1>::type::const_iterator it = data.get<1>().begin(); N < 10 && it != data.get<1>().end(); ++it, ++N ) {
// copy elements somewhere
}
}
You could use any index for iteration ( val_str or val_int ).
May not be the most elegant way, but you can sort them via value in a set as:
#include <map>
#include <set>
#include <iostream>
#include <string>
using namespace std;
struct sortPairSecond
{
bool operator()(const pair<string, int> &lhs, const pair<string, int> &rhs)
{
return lhs.second < rhs.second;
}
};
int main (int argc, char *argv[])
{
cout << "Started...\n";
map<string, int> myMap;
myMap["One"] = 1;
myMap["Ten"] = 10;
myMap["Five"] = 5;
myMap["Zero"] = 0;
myMap["Eight"] = 8;
cout << "Map Order:\n---------------\n";
set<pair<string,int>, sortPairSecond > mySet;
for(map<string, int>::const_iterator it = myMap.begin(); it != myMap.end(); ++it)
{
cout << it->first << " = " << it->second << "\n";
mySet.insert(*it);
}
cout << "\nSet Order:\n--------------\n";
for(set<pair<string, int> >::const_iterator it = mySet.begin(); it != mySet.end(); ++it)
{
cout << it->first << " = " << it->second << "\n";
}
return 1;
}
If you iterate using the map iterator, you will get the items sorted on key as it internally uses balanced binary tree to store the values. So you could just extract the 10 values from it using the iterators. Is that what you want or you want to do something else? Please clarify.
EDIT:
Instead of using the vector and sorting, you can directly use set and pass the comparison function. Then you can extract the top 10 elements. This is my test code:
typedef std::pair<std::string, int> MyPair;
struct MyTestCompare
{
bool operator()(const MyPair& firstPair, const MyPair& secondPair) const
{
return firstPair.second < secondPair.second;
}
};
int main()
{
std::map<std::string, int> m;
m[std::string("1")] = 10;
m[std::string("2")] = 40;
m[std::string("3")] = 30;
m[std::string("4")] = 20;
std::set<MyPair,MyTestCompare> s;
std::map<std::string, int>::iterator iter = m.begin();
std::map<std::string, int>::iterator endIter = m.end();
for(; iter != endIter; ++iter)
{
s.insert(*iter);
}
}
Another possibility is to build a reverse map. For you that would be std::map<int, std::string>. Entries in the reverse map are sorted by their value.
The following is what I have in my tool box for such occasions:
template< typename TK, typename TV, class TP, class TA, typename T1, typename T2 >
inline void asserted_insert(std::map<TK,TV,TP,TA>& m, const T1& k, const T2& v)
{
typedef std::map<TK,TV,TP,TA> map_type;
typedef typename map_type::value_type value_type;
assert( m.insert(value_type(k,v)).second );
}
template< class TMap > struct reverse_map;
template< typename T1, typename T2 > struct reverse_map< std::map<T1,T2> > {
typedef std::map<T2,T1> result_t;
};
template< typename T1, typename T2, class TP1, class TA1, class TP2, class TA2 >
inline void build_reverse_map(const std::map<T1,T2,TP1,TA1>& map, std::map<T2,T1,TP2,TA2>& reverse_map)
{
typedef std::map<T1,T2,TP1,TA1> map_type;
for( typename map_type::const_iterator it=map.begin(),
end=map.end(); it!=end; ++it ) {
asserted_insert( reverse_map, it->second, it->first );
}
}
This code assumes that values are unique, too (and throws an assertion, if this is not the case). If this doesn't apply to your problem, you could easily change the code to use a multi map.