C++ boost::range::remove_erase does not work on std::map? - c++

The code snippet
std::map<int, int> m = { { 1, 2 }, { 3, 4 } };
boost::range::remove_erase_if(
m,
[](const auto& it)
{
return it.first == 1;
});
produces the error
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133
\include\xmemory(1986,1): error C2679: binary '=': no operator found which takes a right-hand operand of type 'std::pair<const int,int>' (or there is no acceptable conversion)
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133\include\utility(269,11): message : could be 'std::pair<const int,int> &std::pair<const int,int>::operator =(volatile const std::pair<const int,int> &)'
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133\include\xmemory(1986,1): message : while trying to match the argument list '(std::pair<const int,int>, std::pair<const int,int>)'
I expected remove_erase_if to remove the first pair {1, 2} from the map.
remove_erase_if behaves as expected when I try it with a vector instead of a map:
std::vector<int> v = { 1, 2, 3, 4 };
boost::range::remove_erase_if(
v,
[](const auto& it)
{
return it == 1;
});
Here v contains {2, 3, 4} after execution. What do I have to adjust, to use remove_erase_if with a map?

In your case boost::remove_erase_if will do the same as
m.erase(std::remove_if(m.begin(), m.end(), [](const auto& it) { return it.first == 1; }), m.end());
which doesn't work well on std::maps.
If you can't use C++20 std::erase_if(std::map), make your own. When you later upgrade to C++20, you can replace the dnx namespace with std in your code that uses dnx::erase_if.
namespace dnx {
template <class Key, class T, class Compare, class Alloc, class Pred>
typename std::map<Key, T, Compare, Alloc>::size_type erase_if(
std::map<Key, T, Compare, Alloc>& c, Pred pred) {
auto old_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last;) {
if (pred(*i)) {
i = c.erase(i);
} else {
++i;
}
}
return old_size - c.size();
}
} // namespace dnx
int main() {
std::map<int, int> m = {{1, 2}, {3, 4}};
// now works:
dnx::erase_if(m, [](const auto& it) { return it.first == 1; });
}

Related

Finding a key-value-pair within a std::multimap with key of type int

I have a std::multimap<int, X*> where X is a user-defined type. I want to find a specific key-value-pair within this multimap (i.e. an iterator pointing to this pair).
(A) Complete Example:
#include <map>
#include <algorithm>
class X {};
int main()
{
X myX;
std::multimap<int, X*> myMap;
auto it = std::find(myMap.begin(), myMap.end(), std::make_pair(5, &myX));
return 0;
}
However, this does not compile (gcc 12.2.1 with -std=gnu++2a):
no match for ‘operator==’ (operand types are ‘std::pair<const int, X*>’ and ‘const std::pair<int, X*>’)
So it seems to me somehow int gets converted to const int.
(B) Using std::find_if with a lamdba function with const int, the code compiles:
auto it = std::find_if(myMap.begin(), myMap.end(), [myX](std::pair<const int, X*>& node){ return 5 == node.first && (&myX) == node.second; } );
Questions:
Why is the type of the keys in the multimap const int and not int?
How to fix it in a more natural way than using a (complex) lambda function like in (B) or by first looking up by key and then searching within its values (as described by Igor Tandetnik in the comments)?
Why is the type of the keys in the multimap const int and not int?
Because the Key in all standard maps is immutable.
How to fix it in a more natural way than using a (complex) lambda function like in (B) or by first looking up by key and then searching within its values?
Here's a simplified example that doesn't use X* but X in the map:
#include <algorithm>
#include <iostream>
#include <map>
struct X {
bool operator==(const X& rhs) const { return m_value == rhs.m_value; }
int m_value;
};
int main() {
std::multimap<int, X> myMap{
{4, {1}}, {5, {2}}, {6, {3}}, {4, {4}}, {5, {5}}, {6, {6}}
};
// get the range of _Key_s equal to 5
auto [first, last] = myMap.equal_range(5);
// the lambda - here used to find the first entry with the _Value_ `X{2}`
auto cond = [](auto&& pair) { return pair.second == X{2}; };
if (auto it = std::find_if(first, last, cond); it != last)
{
auto& [k, v] = *it;
std::cout << k << ' ' << v.m_value << '\n';
}
}
Demo
If many Values may be found, just put the find_if in a loop:
for(;(first = std::find_if(first, last, cond)) != last; ++first) {
auto& [k, v] = *first;
std::cout << k << ' ' << v.m_value << '\n';
}
Demo

in-place coalescing like elements in a std::vector

I have an array of pairs, for example:
X = {{A, 1}, {B, 2}, {C, 1}, {A, 3}, {C, 4}}
I would like to produce an array:
Y = (x, n) such that n = sum i for (x, i) in X
so in the example above, we'd have:
Y = {{A, 4}, {B, 2}, {C, 5}}
The code I currently have is:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
char A = 'A';
char B = 'B';
char C = 'C';
vector< pair<char, int> > X = {{A, 1}, {B, 2}, {C, 1}, {A, 3}, {C, 4}};
// Sort by first element of the pair
sort(begin(X), end(X), [](auto a, auto b) { return a.first < b.first; });
// Could this be better? Is there an existing STL algorithm that will
// do this in-place?
vector< pair<char, int> > Y;
for(auto p : X) {
if(Y.empty() || Y.back().first != p.first) {
Y.push_back(p);
} else {
Y.back().second += p.second;
}
}
cout << "Y:";
for (auto p : Y) {
cout << '{' << p.first << ' ' << p.second << '}';
}
cout << '\n';
}
Could this code be made more succinct? (Without changing the type of the underlying container)
I'm looking to try to eliminate the raw loop, by replacing with an algorithm in the standard library, but I don't see one that would quite fit.
I'd want some variant of std::unique which takes not only a predicate for whether two elements are equivalent, but also a function which defines how to combine them. It might look like:
coalesce(begin(X), end(X), [](auto a, auto b){ return a.first == b.first; }, [](auto a, auto b) { return {a.first, a.second+b.second} });
FWIW, here's an implementation of coalesce which seems to work:
template<class ForwardIt, class BinaryPredicate, class BinaryFunction>
ForwardIt coalesce(ForwardIt first, ForwardIt last, BinaryPredicate p, BinaryFunction f)
{
if (first == last)
return last;
ForwardIt result = first;
while (++first != last) {
if(p(*result, *first)) {
*result = f(*result, *first);
} else {
++result;
*result = *first;
}
}
return ++result;
}
And the code becomes:
vector< pair<char, int> > X = {{A, 1}, {B, 2}, {C, 1}, {A, 3}, {C, 4}};
// Sort by first element of the pair
sort(begin(X), end(X), [](auto a, auto b) { return a.first < b.first; });
// Easier to understand the intent!
auto e = coalesce(begin(X), end(X),
[](auto a, auto b) { return a.first == b.first; },
[](auto a, auto b) { return pair<char, int>{a.first, a.second+b.second}; });
for_each(begin(X), e, [](auto p) {
cout << '{' << p.first << ' ' << p.second << '}';
});
cout << '\n';
NOTE: I'm quite familiar with map, etc, and don't want to use it.
Hm, an approach that uses no other containers, with no raw loops (or std::for_each) might combine std::sort with std::partial_sum
std::partial_sum is for computing prefix sums, or rather a generic way to combine adjacent elements. After our initial sort, we can use std::partial_sum to combine elements with the same key:
std::vector< std::pair<char, int> > Y;
std::vector< std::pair<char, int> > Y(X.size());
std::partial_sum(X.begin(), X.end(), Y.rbegin(), [](const auto& lhs, const auto& rhs)
{
if (lhs.first != rhs.first)
return rhs;
return std::make_pair(lhs.first, lhs.second + rhs.second);
});
Notice that we iterate backwards in Y. This is intentional for the next step, which I'll elaborate shortly.
This gets us part of the way there. Now we have a Y that looks like this:
Y:{C 5}{C 1}{B 2}{A 4}{A 1}
Now our task is to remove the duplicates, which we can do with std::unique:
Y.erase(std::unique(Y.begin(), Y.end(),
[](const auto& lhs, const auto& rhs){
return lhs.first == rhs.first;}), Y.end());
We needed to use partial_sum over the reversed range, because std::unique "Eliminates all but the first element from every consecutive group of equivalent elements", and we needed the final partial_sum to appear first.
The total algorithm is O(N log N) on account of the sorting. Memory usage is O(N).
Demo
I'd be tempted to define it in terms of a Compare, not equals. You'd std::upper_bound to get groups and std::accumulate within each group.
template<class ForwardIt, class OutputIt, class Compare = std::less<>, class BinaryOperation = std::plus<>>
OutputIt coalesce(ForwardIt first, ForwardIt last, OutputIt d_first, Compare comp = {}, BinaryOperation op = {})
{
while (first != last) {
ForwardIt group = std::upper_bound(first, last, *first, comp);
*d_first++ = std::accumulate(std::next(first), group, *first, op);
first = group;
}
return d_first;
}
Which would be used like
vector< pair<char, int> > X = {{'A', 1}, {'B', 2}, {'C', 1}, {'A', 3}, {'C', 4}};
less<> comp;
auto add = [](auto a, auto b) { return pair<char, int>{a.first, a.second+b.second}; };
sort(begin(X), end(X)/*, comp*/);
auto e = coalesce(begin(X), end(X), begin(X), comp, add);
X.erase(e, end(X));
for (auto [k, v] : X) {
cout << '{' << k << ' ' << v << '}';
}
(Note: OP edited the question after my answer to specify they didn't want to use a map or its variations, and then again to specify it needed to be in-place)
A hash table will do the coalescing work for you:
std::unordered_map<char, int> coalesced;
for(const auto key_val : X)
coalesced[key_val.first] += key_val.second;
Now we have a hash table with contents of
A : 4
B : 2
C : 5
If you want to put that into another std::vector, that's fine:
vector< pair<char, int> > Y(coalesced.begin(), coalesced.end());
Or you could leave as-is.
The unordered_map is unsorted w.r.t keys (hence the "unordered" name). If you want them ordered, then you can just use a std::map exactly the same way (but it's implemented as a binary search tree rather than a hash table)
Demo

Minimum return value for range

I was wondering if there was a standard function that returns the minimum/maximum of the return values for a given range of elements. Something like this:
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it =
std::find_min_return_value(list.begin(), list.end(), std::abs, std::less<int>);
// it should be the iterator for -1
If there is no such, what is the best approach for a problem like this?
My list is long, I really don't want to copy it, and also don't want to call the function whose minimum return value I look for more than once per element. Thanks!
UPDATE:
Based on ForEveR's suggestion to use std::min_element, I made the following benchmarking tests:
std::vector<double> list = { -2, -1, 6, 8, 10 };
auto semi_expensive_test_function = [] (const double& a) { return asin(sin(a)); };
for(int i = 0; i < 10000000; ++i)
{
auto it = std::min_element(list.begin(), list.end(),
[&] (const double& a, const double& b) mutable
{
return(semi_expensive_test_function(a) < semi_expensive_test_function(b));
});
}
This worked just fine:
./a.out 11.52s user 0.00s system 99% cpu 11.521 total
After modifying the code to use a stateful lambda instead:
for(int i = 0; i < 10000000; ++i)
{
auto it = std::min_element(list.begin() + 1, list.end(),
[&, current_min_value = semi_expensive_test_function(*(list.begin()))] (const double& a, const double& b) mutable
{
double current_value = semi_expensive_test_function(b);
if(current_value < current_min_value)
{
current_min_value = std::move(current_value);
return true;
}
return false;
});
}
This resulted:
./a.out 6.34s user 0.00s system 99% cpu 6.337 total
Using stateful lambdas seems to be the way to go. The question is: is there a more code-compact way to achieve this?
With range-v3, it would be something like:
ranges::min(list, std::less<>{}, [](auto e) { return std::abs(e); });
Well, assuming Boost is like the standard library nowadays, you might use this:
#include <boost/range/adaptor/transformed.hpp>
#include <algorithm>
int main()
{
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto abs_list = list | boost::adaptors::transformed(+[](int v) { return std::abs(v); });
// ^ - read http://stackoverflow.com/questions/11872558/using-boost-adaptors-with-c11-lambdas
auto it = std::min_element(abs_list.begin(), abs_list.end(), std::less<int>{});
std::cout << *it;
}
If it'll get reused, and to give you another option, you could write your own generic algorithm following std conventions.
template <typename T, typename ForwardIt, typename UnaryFunction, typename Comparator>
ForwardIt find_min_return_value(ForwardIt first, ForwardIt last, UnaryFunction op, Comparator compare)
{
if (first == last)
return last;
ForwardIt smallestItr = first;
T smallestValue = op(*first);
for (auto itr = first + 1; itr != last; ++itr)
{
T current = op(*itr);
if (compare(current, smallestValue))
{
smallestValue = current;
smallestItr = itr;
}
}
return smallestItr;
}
Usage is then quite code-compact, and the operation will only be performed once per element:
int main()
{
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it1 = find_min_return_value<int>(list.begin(), list.end(), [](int i){ return std::abs(i); }, std::less<int>());
std::vector<std::string> strings = { "Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit" };
auto it3 = find_min_return_value<size_t>(strings.begin(), strings.end(), [](std::string s){ return s.length(); }, std::less<size_t>());
std::cout << *it1 << "\n"; // outputs -1
std::cout << *it3 << "\n"; // outputs sit
}
If you only suspect it may get reused one day it probably won't, and it's then overly complicated, and should then be just a simple function.

Idiomatic and efficient way to add two ranges element-wise

Is there any efficient and idiomatic way to perform the following operation?
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
for (std::size_t i = 0 ; i < a.size() ; ++i)
{
a[i] += b[i];
}
I am trying to avoid the brackets/index notation and only use iterators in order for the operation to work with any container with forward iterators. I thought of the following solution:
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
std::transform(a.begin(), a.end(),
b.begin(),
a.begin(),
std::plus<int>());
However, there is the redundancy of a.begin() and I only get to use + and not +=. Is there some algorithm in the standard library that would allow me to use iterators without having any redundancy or do I have to write the full loops by hand?
Perhaps something that was intended to become idiomatic, but never quite did:
std::valarray<int> a = { 1, 2, 3, 4 };
std::valarray<int> b = { 5, 6, 7, 8 };
Now you can do these
std::valarray<int> c = a + b; //apply '+' element-wise; store result in c
a += b; //apply '+=' element-wise
See the documentation of std::valarray for more details.
What's wrong with the redundancy of a.begin()?
If you're not happy with it, just invent your own algorithm: transform_inplace
template <class InputIterator, class OutputIterator, class BinaryOperator>
OutputIterator transform_inplace (InputIterator first,
InputIterator last,
OutputIterator result,
BinaryOperator op)
{
while (first != last) {
*result = op(*result, *first);
++result;
++first;
}
return result;
}
If you were to use this more than once, and you were interested in a simple interface in the spirit of the Standard Library, you could create a simple template class for the specific use case (which I refer to as "Range Increment"), and write something like:
#include<vector>
#include<algorithm>
#include<iostream>
template<typename InputIt>
InputIt range_increment(InputIt dbeg, InputIt dend, InputIt sbeg) {
while(dbeg!=dend) {
*(dbeg++) += (*sbeg++);
}
return dbeg;
}
int main() {
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
range_increment(a.begin(), a.end(), b.begin());
for(auto x:a) {
std::cout<<x<<std::endl;
}
}
Which yields:
6
8
10
12
Not sure I'd call it "idiomatic", but:
assert(a.size()==b.size());
auto bi = b.begin();
for (auto& i : a) {
i += *(bi++);
}
is pretty concise.
I couldn't find the kind of generic function I was looking for and finally went with the following function that I named range_map ("map the given function element-wise with two given ranges"). As the comments point, it is actually no more than a binary std::for_each:
template<class InputIt1, class InputIt2, class BinaryOperation>
void range_map(InputIt1 first1, InputIt1 last1,
InputIt2 first2, BinaryOperation binary_op)
{
while (first1 != last1) {
binary_op(*first1++, *first2++);
}
}
I created the class plus_assign the following way:
template<typename T>
struct plus_assign
{
void operator()(T &lhs, const T &rhs) const
{
lhs += rhs;
}
};
And then my code becomes:
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
range_map(a.begin(), a.end(),
b.begin(),
plus_assign<int>());
There is also the unary counterpart of the function range_map, to map a given functor to a range:
template<class InputIt, class BinaryOperation>
void range_map(InputIt first, InputIt last,
UnaryOperation unary_op)
{
while (first != last) {
unary_op(*first1++);
}
}
Use operator overload
#include <vector>
std::vector<int>& operator += (std::vector<int>& a, std::vector<int> const& b)
{
for(size_t i = 0; i != a.size(); ++i)
a[i] += b[i];
return a;
}
int main(int argc, char * argv[])
{
std::vector<int> a { 1, 3, 5, 7, 9};
std::vector<int> b { 2, 4, 6, 8, 10};
a += b;
return 0;
}

How can I sort an STL map by value?

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?
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.
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.
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.
You should use Boost.Bimap for this sort of thing.
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.
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;
}
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.
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;
}
One thing that could be done in some scenarios, is using a vector<pair<int, int>> rather than using maps<int, int>. In this way you lose the benefits of using map, such as the less lookup time but you can directly use comparator function with vector<pair<int, int>>
bool compare(pair<int, int> a, pair<int, int> b)
{
return (a.second < b.second);
}
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);
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;
}
}