By definition you cannot sort a std::map
by value, since a std::map
sorts its elements by key.
This posting documents a way to get around this by dumping std::map
key-value pairs into a std::vector
first, then sorting that std::vector
with a less-than functor afterwards
Example 1: mapping between std::pair
and double
I use the STL map to create a one-to-one mapping between a pair of link node IDs (using a std::pair
) and the weight value connecting these nodes (using a double):
std::map<std::pair<int,int>, double>; link_map;
The first step then is to physically create the mappings:
// Create mapping between node pairs and weights link_map[ std::pair<int,int>(0,1) ] = 1.1; link_map[ std::pair<int,int>(1,2) ] = 0.1; link_map[ std::pair<int,int>(2,3) ] = 6.2; link_map[ std::pair<int,int>(3,4) ] = 3.4; link_map[ std::pair<int,int>(4,5) ] = 5.7; link_map[ std::pair<int,int>(5,6) ] = 2.2; link_map[ std::pair<int,int>(0,8) ] = 1.8;
So that the std::pair
node IDs and their respective weights are printed as follows. Notice that since the std::pair
is used as the map key, the map items are automatically sorted according to this, not the weight values:
0 1 1.1 0 8 1.8 1 2 0.1 2 3 6.2 3 4 3.4 4 5 5.7 5 6 2.2
Given that for the specfic problem I was working on, I needed some means of obtaining the mappings so that they were sorted according to their weights, not their std::pair
key values. One way to do this is not so much try to sort the std::map
itself (which you can’t), but to use a function to insert these mappings into a std::vector
and sort the std::vector
items. An an additional functor which which to define how the items are sorted is also defined:
template<class T> struct less_second : std::binary_function<T,T,bool> { inline bool operator()( const T& lhs, const T& rhs ) { return lhs.second < rhs.second; } }; // Initialize and sort vector with data_t items from link_map std::vector<data_t> sorted_edges() { std::vector< data_t > vec(link_map.begin(), link_map.end()); std::sort(vec.begin(), vec.end(), less_second<data_t>()); return vec; }
… As is data
_t, an additional typedef
used to define the mapping:
typedef std::pair<std::pair<int,int>, double> data_t;
So that when we print out the vector of data_t items, we can see that they are sorted according to the second map parameter (double), and not the std::pair
representing node IDs:
1 2 0.1 0 1 1.1 0 8 1.8 5 6 2.2 3 4 3.4 4 5 5.7 2 3 6.2
Full code listing
#include <map> #include <vector> #include <iostream> #include <algorithm> typedef std::pair<std::pair<int,int>, double> data_t; std::map<std::pair<int,int>, double> link_map; std::map<std::pair>int,int>, double>::iterator link_it; template<class T> struct less_second : std::binary_function<T,T,bool> { inline bool operator()( const T& lhs, const T& rhs ) { return lhs.second < rhs.second; } }; // Initialize and sort vector with data_t items from link_map std::vector<data_t> sorted_edges() { std::vector< data_t > vec(link_map.begin(), link_map.end()); std::sort(vec.begin(), vec.end(), less_second<data_t>()); return vec; } void PrintLinkMap() { std::cout << std::endl; for ( link_it = link_map.begin(); link_it != link_map.end(); link_it++ ) { std::pair<int,int> p = (*link_it).first; double weight = (*link_it).second; std::cout << p.first << " " << p.second << " " << weight << std::endl; } } void PrintSortedEdges( const std::vector<data_t>& vec ) { std::vector<data_t>::const_iterator vec_it = vec.begin(); std::cout << std::endl; for ( ; vec_it != vec.end(); vec_it++ ) { std::pair<int,int> p = (*vec_it).first; double weight = (*vec_it).second; std::cout << p.first << " " << p.second << " " << weight << std::endl; } } int main() { // Create mapping between node pairs and weights link_map[ std::pair<int,int>(0,1) ] = 1.1; link_map[ std::pair<int,int>(1,2) ] = 0.1; link_map[ std::pair<int,int>(2,3) ] = 6.2; link_map[ std::pair<int,int>(3,4) ] = 3.4; link_map[ std::pair<int,int>(4,5) ] = 5.7; link_map[ std::pair<int,int>(5,6) ] = 2.2; link_map[ std::pair<int,int>(0,8) ] = 1.8; PrintLinkMap(); // Sort the mapping according to link weight // and return in the form of a vector std::vector<data_t> vec = sorted_edges(); PrintSortedEdges( vec ); return 0; }
Example 2: mapping between object pointer and double
Here is another example, except this time we use a mapping between a pointer to an object and a double
value. We use the same generic functor for sorting the vector:
#include <map> #include <vector> #include <iostream> #include <algorithm> class A { private: int data; public: A(const int& d) : data(d) {} int val() const { return data; } }; typedef std::pair<A*, double> data_a; std::map<A*, double> a_map; std::map<*, double>::iterator a_it; template<class T> struct less_second : std::binary_function<T,T,bool> { inline bool operator()( const T& lhs, const T& rhs ) { return lhs.second < rhs.second; } }; // Initialize and sort vector with data_t items from link_map std::vector<data_a> sort_by_weight() { std::vector< data_a > vec(a_map.begin(), a_map.end()); std::sort(vec.begin(), vec.end(), less_second<data_a>()); return vec; } void Print() { std::cout << std::endl; for ( a_it = a_map.begin(); a_it != a_map.end(); a_it++ ) { A* a = (*a_it).first; double weight = (*a_it).second; std::cout << a->val() << " " << weight << std::endl; } } void PrintSorted( const std::vector<data_a>& vec ) { std::vector<data_a>::const_iterator vec_it = vec.begin(); std::cout << std::endl; for ( ; vec_it != vec.end(); vec_it++ ) { A* a = (*vec_it).first; double weight = (*vec_it).second; std::cout << a->val() << " " << weight << std::endl; } } int main() { // Create mapping between class A objects and doubles a_map[ new A(23) ] = 2.1; a_map[ new A(44) ] = 1.2; a_map[ new A(55) ] = 1.1; a_map[ new A(25) ] = 2.2; a_map[ new A(71) ] = 0.2; Print(); // Sort the mapping according to link weight // and return in the form of a vector std::vector<data_a> vec = sort_by_weight(); PrintSorted( vec ); return 0; }
Giving the following results, before and after we have sorted the std:map
items:
23 2.1
44 1.2
55 1.1
25 2.2
71 0.2
71 0.2
55 1.1
44 1.2
23 2.1
25 2.2
Example 3: mapping between std::string
and double
And one more example, this time using mappings between std::string
s and doubles:
#include <map> #include <vector> #include <iostream> #include <algorithm> typedef std::pair<std::string, double> data_s; std::map<std::string, double> s_map; std::map<std::string, double>::iterator s_it; template<class T> struct less_second : std::binary_function<T,T,bool> { inline bool operator()( const T& lhs, const T& rhs ) { return lhs.second < rhs.second; } }; // Initialize and sort vector with data_t items from link_map std::vector<data_s> sort_by_weight() { std::vector< data_s > vec(s_map.begin(), s_map.end()); std::sort(vec.begin(), vec.end(), less_second<data_s>()); return vec; } void Print() { std::cout << std::endl; for ( s_it = s_map.begin(); s_it != s_map.end(); s_it++ ) { std::string s = (*s_it).first; double weight = (*s_it).second; std::cout << s << " " << weight << std::endl; } } void PrintSorted( const std::vector<data_s>& vec ) { std::vector<data_s>::const_iterator vec_it = vec.begin(); std::cout << std::endl; for ( ; vec_it != vec.end(); vec_it++ ) { std::string s = (*vec_it).first; double weight = (*vec_it).second; std::cout << s << " " << weight << std::endl; } } int main() { // Create mapping between classstd::string and doubles s_map[ "Matthew" ] = 2.1; s_map[ "Mark" ] = 1.2; s_map[ "Luke" ] = 1.1; s_map[ "John" ] = 2.2; Print(); // Sort the mapping according to link weight // and return in the form of a vector std::vector<data_s> vec = sort_by_weight(); PrintSorted( vec ); return 0; }
Giving the following results:
John 2.2
Luke 1.1
Mark 1.2
Matthew 2.1
Luke 1.1
Mark 1.2
Matthew 2.1
John 2.2