How can I sort an STL map by value?

C++AlgorithmSortingDictionaryStl

C++ Problem Overview


How can I implement STL map sorting by value?

For example, I have a map m:

map<int, int> m;
m[1] = 10;
m[2] = 5;
m[4] = 6;
m[6] = 1;

I'd like to sort that map by m's value. So, if I print the map, I'd like to get the result as follows:

m[6] = 1
m[2] = 5
m[4] = 6
m[1] = 10

How can I sort the map in this way? Is there any way that I can deal with the key and value with sorted values?

C++ Solutions


Solution 1 - C++

Dump out all the key-value pairs into a set<pair<K, V> > first, where the set is constructed with a less-than functor that compares the pair's second value only. That way, your code still works even if your values aren't all distinct.

Or dump the key-value pairs into a vector<pair<K, V> >, then sort that vector with the same less-than functor afterwards.

Solution 2 - C++

You can build a second map, with the first map's values as keys and the first map's keys as values.

This works only if all values are distinct. If you cannot assume this, then you need to build a multimap instead of a map.

Solution 3 - C++

> I wonder how can I implement the STL map sorting by value.

You can’t, by definition. A map is a data structure that sorts its element by key.

Solution 4 - C++

You should use Boost.Bimap for this sort of thing.

Solution 5 - C++

Based on @swegi's idea, I implemented a solution in c++11 using a multimap:

map<int, int> m = {{1, 10}, {2, 5}, {4, 6}, {6, 1}};
multimap<int, int> mm;

for(auto const &kv : m)
    mm.insert(make_pair(kv.second, kv.first));  // Flip the pairs.

for(auto const &kv : mm)
    cout << "m[" << kv.second << "] = " << kv.first << endl;  // Flip the pairs again.

Code on Ideone

I also implemented a C++11 solution based on @Chris' idea using a vector of pairs. For correct sorting, I provide a lambda expression as comparison functor:

map<int, int> m = {{1, 10}, {2, 5}, {4, 6}, {6, 1}};
using mypair = pair<int, int>;

vector<mypair> v(begin(m), end(m));

sort(begin(v), end(v), [](const mypair& a, const mypair& b) { return a.second < b.second; });

for(auto const &p : v)
    cout << "m[" << p.first << "] = " << p.second << endl;

Code on Ideone

The first solution is more compact, but both solutions should have roughly the same performance. Inserting into a multimap is of O(log n), but this has to be done for n entries, resulting in O(n log n). Sorting the vector in the second solution also results in O(n log n).

I also gave a try to @Chris' idea on using a set of pairs. However, it won't work if the values aren't all distinct. Using a functor that compares only the pair's second element doesn't help. If you first insert make_pair(1, 1) into the set and then try to insert make_pair(2, 1), then the second pair won't be inserted, because both pairs are seen as identical by that set. You can see that effect here on Ideone.

Solution 6 - C++

I've just done a similar question in my c++ book. The answer I came up with might not be very efficient though:

int main()
{
	string s;
	map<string, int> counters;

	while(cin >> s)
		++counters[s];

	//Get the largest and smallest values from map
	int beginPos = smallest_map_value(counters);
	int endPos = largest_map_value(counters);

	//Increment through smallest value to largest values found
	for(int i = beginPos; i <= endPos; ++i)
	{
		//For each increment, go through the map...
		for(map<string, int>::const_iterator it = counters.begin(); it != counters.end(); ++it)
		{
			//...and print out any pairs with matching values
			if(it->second == i)
			{
				cout << it->first << "\t" << it->second << endl;
			}
		}
	}
	return 0;
}

//Find the smallest value for a map<string, int>
int smallest_map_value(const map<string, int>& m)
{
	map<string, int>::const_iterator it = m.begin();
	int lowest = it->second;
	for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
	{
		if(it->second < lowest)
			lowest = it->second;
	}
	return lowest;
}

//Find the largest value for a map<string, int>
int largest_map_value(const map<string, int>& m)
{
	map<string, int>::const_iterator it = m.begin();
	int highest = it->second;
	for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
	{
		if(it->second > highest)
			highest = it->second;
	}
	return highest;
}

Solution 7 - C++

Create another map, provide a less() function based on the value not key, AND the function should return true if the value1 <= value2 (not strictly < ). In this case, elements with non-distinct values can be sorted as well.

Solution 8 - C++

I have found this in thispointer. The example sorts a std::map< std::string,int> by all the int values.

#include <map>
#include <set>
#include <algorithm>
#include <functional>
 
int main() {
 
	// Creating & Initializing a map of String & Ints
	std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
			{ "bbb", 62 }, { "ccc", 13 } };
 
	// Declaring the type of Predicate that accepts 2 pairs and return a bool
	typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;
 
	// Defining a lambda function to compare two pairs. It will compare two pairs using second field
	Comparator compFunctor =
			[](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
			{
				return elem1.second < elem2.second;
			};
 
	// Declaring a set that will store the pairs using above comparision logic
	std::set<std::pair<std::string, int>, Comparator> setOfWords(
			mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);
 
	// Iterate over a set using range base for loop
	// It will display the items in sorted order of values
	for (std::pair<std::string, int> element : setOfWords)
		std::cout << element.first << " :: " << element.second << std::endl;
 
	return 0;
}

Solution 9 - C++

Recently had to do this. I ended up using pointers...

Quick Benchmark Results

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

using map_t = std::map<int,int>;
const map_t m
{
    { 5, 20 },
    { -18, 28 },
    { 24, 49 },
    { 17, 27 },
    { 23, 46 },
    { 8, 16 },
    { -13, 11 },
    { -22, 32 },
    { 12, 45 },
    { -2, 19 },
    { 21, 11 },
    { -12, 25 },
    { -20, 8 },
    { 0, 29 },
    { -5, 20 },
    { 13, 26 },
    { 1, 27 },
    { -14, 3 },
    { 19, 47 },
    { -15, 17 },
    { 16, 1 },
    { -17, 50 },
    { -6, 40 },
    { 15, 24 },
    { 9, 10 }
};

template<typename T>
void sort_values_using_vector(T const& m)
{
    using map_t = T;

    using sort_t = std::vector<std::pair<typename map_t::key_type,
        typename map_t::mapped_type>>;
    sort_t sorted{ m.begin(), m.end() };
    
    std::sort(sorted.begin(), sorted.end(),
        [](auto const& lhs, auto const& rhs)
        {
            return lhs.second < rhs.second;
        });
}

template<typename T>
void sort_values_using_multimap(T const& m)
{
    using map_t = T;

    using sort_t = std::multimap<typename map_t::mapped_type,
        typename map_t::key_type>;
    sort_t sorted;

    for (auto const& kv : m)
    {
        sorted.insert(std::make_pair(kv.second, kv.first));
    }
}

template<typename T>
void sort_values_using_ptrs(T const& m)
{
    using map_t = T;

    using ptr_t = std::add_pointer_t
        <std::add_const_t<typename map_t::value_type>>;
    using sort_t = std::vector<ptr_t>;
    sort_t sorted;
    sorted.reserve(m.size());

    for (auto const& kv : m)
    {
        sorted.push_back(std::addressof(kv));
    }

    std::sort(sorted.begin(), sorted.end(),
        [](auto const& lhs, auto const& rhs)
        {
            return lhs->second < rhs->second;
        });
}

template<typename T>
void sort_values_using_refs(T const& m)
{
    using map_t = T;

    using ref_t = std::reference_wrapper
        <std::add_const_t<typename map_t::value_type>>;
    using sort_t = std::vector<ref_t>;
    sort_t sorted{ m.begin(), m.end() };

    std::sort(sorted.begin(), sorted.end(),
        [](auto const& lhs, auto const& rhs)
        {
            return lhs.get().second < rhs.get().second;
        });
}

static void copy_to_vector(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    sort_values_using_vector(m);
  }
}
BENCHMARK(copy_to_vector);

static void copy_flipped_to_multimap(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    sort_values_using_multimap(m);
  }
}
BENCHMARK(copy_flipped_to_multimap);

static void copy_ptrs_to_vector(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    sort_values_using_ptrs(m);
  }
}
BENCHMARK(copy_ptrs_to_vector);

static void use_refs_in_vector(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    sort_values_using_refs(m);
  }
}
BENCHMARK(use_refs_in_vector);

Solution 10 - C++

This code uses custom sorting function to sort map by values

// Comparator function to sort pairs
// according to value
bool comp(pair<int, int>& a,
        pair<int, int>& b)
{
    return a.second < b.second;
}

// Function to sort the map according
// to value in a (key-value) pair
void customSort(map<int, int>& m)
{

    vector<pair<int, int>> a;

    for(auto x:m)
        a.push_back(make_pair(x.first,x.second));

    sort(a.begin(), a.end(), comp);

    for (auto x:a) {
        cout << x.first<<" "<<x.second<<endl;
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCharlie EppsView Question on Stackoverflow
Solution 1 - C++Chris Jester-YoungView Answer on Stackoverflow
Solution 2 - C++swegiView Answer on Stackoverflow
Solution 3 - C++Konrad RudolphView Answer on Stackoverflow
Solution 4 - C++rlbondView Answer on Stackoverflow
Solution 5 - C++honkView Answer on Stackoverflow
Solution 6 - C++hatethisgarbageView Answer on Stackoverflow
Solution 7 - C++Hua ZhongView Answer on Stackoverflow
Solution 8 - C++Pat. ANDRIAView Answer on Stackoverflow
Solution 9 - C++Mercury DimeView Answer on Stackoverflow
Solution 10 - C++code_10View Answer on Stackoverflow