Consider a std::map<K,V>. I want to re-order the map by value profiting by an appropriate container std::C<V*> or std::C<V&>, in a way that no copies of values are done to store the elements in C. Furthermore, elements in C must be sorted according to the result of int f(V&) applied to each element. Despite my efforts I could not find an appropriate C and an enough efficient way to build it. Do you have any solution? A small example would be much appreciated.
Seems simple enough.
std::map<K,V> src;
int f(V&) {return 0;}
V* get_second(std::pair<const K,V> &r) {return &(r.second);} //transformation
bool pred(V* l, V* r) { return f(*l)<f(*r); } //sorting predicate
std::vector<V*> dest(src.size()); //make destination big enough
std::transform(src.begin(), src.end(), dest.begin(), get_second); //transformcopy
std::sort(dest.begin(), dest.end(), pred); //sort
Unless you meant C is supposed to be another map:
std::pair<K,V*> shallow_pair(std::pair<const K,V> &r)
{return std::pair<K,V*>(r.first, &(r.second));}
std::map<K, V*> dest2;
std::transform(src.begin(), src.end(),
std::inserter(dest2,dest2.end()), shallow_pair);
http://ideone.com/bBoXq
This requires the previous map to remain in scope longer than dest, and have no pairs removed until dest is destructed. Otherwise src will need to have been holding smart pointers of some sort.
You can use std::reference_wrapper, like this:
#include <map>
#include <string>
#include <algorithm>
#include <functional>
#include <prettyprint.hpp>
#include <iostream>
template <typename T>
std::ostream & operator<<(std::ostream & o, std::reference_wrapper<T> const & rw)
{
return o << rw.get();
}
int main()
{
std::map<int, std::string> m { { 1, "hello"}, { 2, "aardvark" } };
std::cout << m << std::endl;
std::vector<std::reference_wrapper<std::string>> v;
for (auto & p : m) v.emplace_back(p.second);
std::cout << v << std::endl;
std::sort(v.begin(), v.end(), std::less<std::string>); // or your own predicate
std::cout << v << std::endl;
v.front().get() = "world";
std::cout << m << std::endl;
}
This prints:
[(1, hello), (2, aardvark)]
[hello, aardvark]
[aardvark, hello]
[(1, hello), (2, world)]
Sounds like you are using multiple containers to represent multiple views into the same dataset. The trouble with this approach is in keeping the containers synchronized and avoiding dangling pointer issues. Boost.MultiIndex was made for just this purpose. A boost::multi_index container stores only one copy of each element, but allows you to access the elements via several indices.
Example:
#include <iterator>
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/global_fun.hpp>
#include <boost/multi_index/member.hpp>
typedef std::string Key;
typedef int Value;
struct Record
{
Record(const Key& key, Value value) : key(key), value(value) {}
Key key;
Value value;
};
inline std::ostream& operator<<(std::ostream& os, const Record& rec)
{
os << rec.key << " " << rec.value << "\n";
return os;
}
inline int sortFunc(const Record& rec) {return -rec.value;}
struct ByNumber{}; // tag
namespace bmi = boost::multi_index;
typedef bmi::multi_index_container<
Record,
bmi::indexed_by<
// sort by key like a std::map
bmi::ordered_unique< bmi::member<Record, Key, &Record::key> >,
// sort by less<int> on free function sortFunc(const Record&)
bmi::ordered_non_unique<bmi::tag<ByNumber>,
bmi::global_fun<const Record&, int, &sortFunc> >
>
> RecordSet;
typedef RecordSet::index<ByNumber>::type RecordsByNumber;
int main()
{
RecordSet rs;
rs.insert(Record("alpha", -1));
rs.insert(Record("charlie", -2));
rs.insert(Record("bravo", -3));
RecordsByNumber& byNum = rs.get<ByNumber>();
std::ostream_iterator<Record> osit(std::cout);
std::cout << "Records sorted by key:\n";
std::copy(rs.begin(), rs.end(), osit);
std::cout << "\nRecords sorted by sortFunc(const Record&):\n";
std::copy(byNum.begin(), byNum.end(), osit);
}
Result:
Records sorted by key:
alpha -1
bravo -3
charlie -2
Records sorted by sortFunc(const Record&):
alpha -1
charlie -2
bravo -3
The place I'd start is to:
look at Boost::bimap
create ordering from V -> K via a comparator function object which evaluates f(V&) the way you suggested. (e.g. as in std::map<K,V,C> where C is a comparator class)
How about,
std::set<boost::shared_ptr<V>, compfunc>
where compfunc is a functor which takes two shared_ptr objects and applies the logic in your function?
Excuse the formatting, not good on my phone.
How about something like this (untested pseudo code):
V* g(pair<K,V> &v) { return &v.second; }
bool cmp(V* a, V* b) { return f(*a) < f(*b); }
map<K,V> map;
vector<V*> vec;
vec.reserve(map.size());
transform(map.begin(), map.end(), back_inserter(vec), g);
sort(vec.begin(), vec.end(), cmp);
Related
I have a unordered_map<int,int> type variable. I want to sort the map according to its value. I was wondering if I can use callback in sort function, so that it returns sorted map according to it's value.
unordered_map<int,int> p;
for(int i=0;i<nums.size();i++){
p[nums[i]]++;
}
sort(p.begin(),p.end(),callback);
You can't sort a unordered_map in-place. You need to build a sortable alternate container, dump the content there (or bring appropriate material necessary to reference the pairs in the original map), then sort away. There are a multitude of ways to accomplish this. A few examples are shown below.
Value Copy and Sort
The following generates one-hundred random draws in the inclusive domain 1..10, then prints the frequency table after sorting based on frequency (value) descending (the unspoken task I suspect to be driving all of this to begin with):
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <random>
int main()
{
std::mt19937 rng{ std::random_device{}() };
std::uniform_int_distribution<> dist(1, 10);
std::unordered_map<int, int> p;
for (int i = 0; i < 100; ++p[dist(rng)], ++i);
std::vector<std::pair<int,int>> v { p.begin(), p.end() };
std::sort(v.begin(), v.end(),
[](auto const& pr1, auto const& pr2)
{ return pr2.second < pr1.second; });
for (auto const& pr : v)
std::cout << pr.first << ':' << pr.second << '\n';
}
Output (varies)
3:14
4:13
7:13
9:11
1:11
2:10
10:7
6:7
8:7
5:7
Pointers to Pairs
An alternate mechanism using constant pointers to the original map pairs is also possible (and probably preferable if the mapped content is either too expensive or outright impossible to make copies of):
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <random>
int main()
{
std::mt19937 rng{ std::random_device{}() };
std::uniform_int_distribution<> dist(1, 10);
std::unordered_map<int, int> p;
for (int i = 0; i < 100; ++p[dist(rng)], ++i);
std::vector<const decltype(p)::value_type*> v;
for (auto const& pr : p)
v.emplace_back(&pr);
std::sort(v.begin(), v.end(),
[](auto const& pr1, auto const& pr2)
{ return pr2->second < pr1->second; });
for (auto const& pr : v)
std::cout << pr->first << ':' << pr->second << '\n';
}
Output (varies)
2:16
1:14
3:11
7:11
4:9
10:9
5:8
8:8
9:8
6:6
Caveats apply to this approach. Those pointers to pairs are only as good as the day they were cashed. E.g., modify the original map by shoving new keys in, or trimming existing keys out, and the entire pointer bed is disavowed. That obviously includes outright destroying the original map. Buyer beware.
You are addressing 2 problems here:
Sorting a std::map or std::unordered_map according to its value
Using a kind of "compare callback" function for std::sort
Let us first tackle the point 2. If we read the description of std::sort in the CPP Reference here, then we see that can use a "comparison function object" as the 3rd parameter. This can be implemented with a Lambda, a functor or with adding a comparison operator to the object that will be sorted.
Let us assume we have a struct in a std::vector that we want to sort.
struct IntVal {
int val{};
};
std::vector<IntVal> intVals{};
If we want to sort this with a Lambda, then:
std::sort(intVals.begin(), intVals.end(), [](const IntVal& iv1, const IntVal& iv2) { return iv1.val < iv2.val; });
Or, you could add a comparison operator to your class/struct:
struct IntVal{
int val{};
bool operator < (const IntVal& other) const { return val < other.val; }
};
Or you can make your class a Functor, by adding a callable operator () to it.
#include <iostream>
#include <vector>
#include <algorithm>
struct IntVal{
int val{};
bool operator () (const IntVal& iv1, const IntVal& iv2) const { return iv1.val < iv2.val; }
};
std::vector<IntVal> intVals{ {3},{2},{1} };
int main() {
std::sort(intVals.begin(), intVals.end(), IntVal());
for (const IntVal& iv : intVals)
std::cout << iv.val << '\n';
}
Or, if you cannot modify the class, the create an external functor:
#include <iostream>
#include <vector>
#include <algorithm>
struct IntVal{
int val{};
};
// Functor
struct Comp {
bool operator () (const IntVal& iv1, const IntVal& iv2) const { return iv1.val < iv2.val; }
};
std::vector<IntVal> intVals{ {3},{2},{1} };
int main() {
std::sort(intVals.begin(), intVals.end(), Comp());
for (const IntVal& iv : intVals)
std::cout << iv.val << '\n';
}
Coming to the next topic.
You want to sort the data, stored in the std::unordered_map by its "value"-type.
This is not possible in place, because the nature of the maps is that they are already sorted. You cannot resort them. This would violate their internal behaviour.
The second problem is often that there key is unique. And sorting could also probaly destroy this strict requirement.
The solution is to use a second container. With you above mentioned "Sort" parameter. Copy the data to a second container and sort this.
We can use a std::vector and its range constructor to copy the data.
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
// Functor
struct Comp {
bool operator () (const std::pair<int,int>& p1, const std::pair<int, int>& p2) { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; }
};
int main() {
std::unordered_map<int, int> test{ {1,4},{2,3},{3,2},{4,1} };
std::vector<std::pair<int, int>> data(test.begin(), test.end());
std::sort(data.begin(), data.end(), Comp());
for (const auto[i1,i2] : data)
std::cout << i1 << ' ' << i2 << '\n';
}
Or, last but not least, and the final solution. We can use a 2nd container that will be sorted by definition, like a std::multiset
This will create a piece of code that is really easy to understand.
Please see:
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
#include <set>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<int, int>;
// The map
using Map = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Sorter = std::multiset<Pair, Comp>;
int main() {
Map test{ {1,4},{2,3},{3,2},{4,1} };
Sorter sorter(test.begin(), test.end());
for (const auto[i1,i2] : sorter)
std::cout << i1 << ' ' << i2 << '\n';
}
And since what you really want to do is counting, please see an example for this as well. Here we will count as an example, the letters in a string:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
#include <cctype>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, unsigned int>;
// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// --------------------------------------------------------------------------------------
// Compact function to calculate the frequency of charcters and then get their rank
Rank getRank(std::string& text) {
// Definition of our counter
Counter counter{};
// Iterate over all charcters in text and count their frequency
for (const char c : text) if (std::isalpha(c)) counter[char(std::tolower(c))]++;
// Return ranks,sorted by frequency and then sorted by character
return { counter.begin(), counter.end() };
}
// --------------------------------------------------------------------------------------
// Test, driver code
int main() {
// Get a string from the user
if (std::string text{}; std::getline(std::cin, text))
// Calculate rank and show result
for (const auto& [letter, count] : getRank(text))
std::cout << letter << " = " << count << '\n';
}
How to sort the multimap using comparator.
It should be reflected in the multimap container
#include <bits/stdc++.h>
using namespace std;
// Comparator function to sort pairs
// according to second value
bool cmp(pair<string, int>& a,
pair<string, int>& b)
{
return a.second < b.second;
}
// Function to sort the map according
// to value in a (key-value) pairs
void sort(multimap<string, int>& M)
{
// Declare vector of pairs
vector<pair<string, int> > A;
// Copy key-value pair from Map
// to vector of pairs
for (auto& it : M) {
A.push_back(it);
}
// Sort using comparator function
sort(A.begin(), A.end(), cmp);
// Print the sorted value
for (auto& it : A) {
cout << it.first << ' '
<< it.second << endl;
}
}
// Driver Code
int main()
{
// Declare Map
multimap<string, int> M;
// Given Map
M = { { "GfG", 3 },
{ "To", 2 },
{ "Welcome", 1 } };
// Function Call
sort(M);
cout<<"\n";
for(auto i:M)
{
cout<<i.first<<" "<<i.second<<endl;
}
return 0;
}
This is code I got from Geekforgeeks!
I totally understood the concept but I need to know how to sort the map itself if the map is multimap.?
OUTPUT
Welcome 1
To 2
GfG 3
GfG 3
To 2
Welcome 1
Basically the answer has been given in the comments already. I will summarize again and show an alternative.
The background is that we often want to use the properties of an associative container, but later sort it by its value and not by the key.
The unsorted associative containers, like std::unsorted_set, std::unordered_map , std::unordered_multiset and std::unordered_multimap cannot be ordered, as their names say. They are using a hash algorithm to store and retrieve their values.
Please read also in the CPP Reference about STL container.
And, if we look at the sorted associative container, we see that the std::set and the std::multiset do not work with key value pairs and the other two, the std::map and std::multimap have a key and value but are always sorted by their key.
But as said, we often want to have the advantages of an Associative container with a key - value pair and later some sorted-by-the-value data.
This can only be acieved by using 2 container having the needed property. In your above example the data is copied into a std::vector and then sorted. This mechanism can be used always, with any sortable container, by adding a std::pair of key and value to a container. So:
using Pair = std::pair<std::string, int>;
using Conatiner = std::vector<Pair>;
An often needed use case is counting something. For example, counting the letters in a string. This can be really easily achieved with a std::map or std::unordered_map and the sorted by the value using a 2nd container. Please see some example code below:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, unsigned int>;
// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset, because their may be double ranks
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// Function to get the rank of letters in a string
Rank getRank(const std::string& str) {
Counter counter;
for (const char c : str) counter[c]++;
return { counter.begin(), counter.end() };
}
// Test / Driver Code
int main() {
if (std::string userString{}; std::getline(std::cin, userString))
for (const auto& [letter, count] : getRank(userString))
std::cout << (int)letter << ' ' << count << ' ';
}
So, we use the property of the std::unordred map as a fast associative container, not sorted by the key, in combination with a std::multiset which will act as a "sorter".
Or, you can take a std::multiset from the beginning. Example:
#include <iostream>
#include <string>
#include <utility>
#include <set>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair <std::string, int> ;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second<p2.second; } };
using Sorted = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// Driver Code
int main()
{
Sorted data { { "GfG", 3 }, { "To", 2 }, { "Welcome", 1 } };
for (const auto& [key, value] : data)
std::cout << key << '\t' << value << '\n';
}
Depends of course all on what you want to achieve . . .
This is one of the possible ways I come out:
struct RetrieveKey
{
template <typename T>
typename T::first_type operator()(T keyValuePair) const
{
return keyValuePair.first;
}
};
map<int, int> m;
vector<int> keys;
// Retrieve all keys
transform(m.begin(), m.end(), back_inserter(keys), RetrieveKey());
// Dump all keys
copy(keys.begin(), keys.end(), ostream_iterator<int>(cout, "\n"));
Of course, we can also retrieve all values from the map by defining another functor RetrieveValues.
Is there any other way to achieve this easily? (I'm always wondering why std::map does not include a member function for us to do so.)
While your solution should work, it can be difficult to read depending on the skill level of your fellow programmers. Additionally, it moves functionality away from the call site. Which can make maintenance a little more difficult.
I'm not sure if your goal is to get the keys into a vector or print them to cout so I'm doing both. You may try something like this:
std::map<int, int> m;
std::vector<int> key, value;
for(std::map<int,int>::iterator it = m.begin(); it != m.end(); ++it) {
key.push_back(it->first);
value.push_back(it->second);
std::cout << "Key: " << it->first << std::endl();
std::cout << "Value: " << it->second << std::endl();
}
Or even simpler, if you are using Boost:
map<int,int> m;
pair<int,int> me; // what a map<int, int> is made of
vector<int> v;
BOOST_FOREACH(me, m) {
v.push_back(me.first);
cout << me.first << "\n";
}
Personally, I like the BOOST_FOREACH version because there is less typing and it is very explicit about what it is doing.
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
for(auto const& imap: mapints)
vints.push_back(imap.first);
There is a boost range adaptor for this purpose:
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
vector<int> keys;
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));
There is a similar map_values range adaptor for extracting the values.
C++0x has given us a further, excellent solution:
std::vector<int> keys;
std::transform(
m_Inputs.begin(),
m_Inputs.end(),
std::back_inserter(keys),
[](const std::map<int,int>::value_type &pair){return pair.first;});
Yet Another Way using C++20
The ranges library has a keys view, which retrieves the first element in a pair/tuple-like type:
#include <ranges>
auto kv = std::views::keys(m);
std::vector<int> keys{ kv.begin(), kv.end() };
Two related views worth mentioning:
values - to get the values in a map (2nd element in a pair/tuple-like type)
elements - to get the nth elements in a tuple-like type
Based on #rusty-parks solution, but in c++17:
std::map<int, int> items;
std::vector<int> itemKeys;
for (const auto& [key, _] : items) {
itemKeys.push_back(key);
}
#DanDan's answer, using C++11 is:
using namespace std;
vector<int> keys;
transform(begin(map_in), end(map_in), back_inserter(keys),
[](decltype(map_in)::value_type const& pair) {
return pair.first;
});
and using C++14 (as noted by #ivan.ukr) we can replace decltype(map_in)::value_type with auto.
Your solution is fine but you can use an iterator to do it:
std::map<int, int> m;
m.insert(std::pair<int, int>(3, 4));
m.insert(std::pair<int, int>(5, 6));
for(std::map<int, int>::const_iterator it = m.begin(); it != m.end(); it++)
{
int key = it->first;
int value = it->second;
//Do something
}
The SGI STL has an extension called select1st. Too bad it's not in standard STL!
I think the BOOST_FOREACH presented above is nice and clean, however, there is another option using BOOST as well.
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
std::map<int, int> m;
std::vector<int> keys;
using namespace boost::lambda;
transform( m.begin(),
m.end(),
back_inserter(keys),
bind( &std::map<int,int>::value_type::first, _1 )
);
copy( keys.begin(), keys.end(), std::ostream_iterator<int>(std::cout, "\n") );
Personally, I don't think this approach is as clean as the BOOST_FOREACH approach in this case, but boost::lambda can be really clean in other cases.
Bit of a c++11 take:
std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto & kvp : items)
{
itemKeys.emplace_back(kvp.first);
std::cout << kvp.first << std::endl;
}
Here's a nice function template using C++11 magic, working for both std::map, std::unordered_map:
template<template <typename...> class MAP, class KEY, class VALUE>
std::vector<KEY>
keys(const MAP<KEY, VALUE>& map)
{
std::vector<KEY> result;
result.reserve(map.size());
for(const auto& it : map){
result.emplace_back(it.first);
}
return result;
}
Check it out here: http://ideone.com/lYBzpL
Also, if you have Boost, use transform_iterator to avoid making a temporary copy of the keys.
You can use the versatile boost::transform_iterator. The transform_iterator allows you to transform the iterated values, for example in our case when you want to deal only with the keys, not the values. See http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example
With the structured binding (“destructuring”) declaration syntax of C++17,
you can do this, which is easier to understand.
// To get the keys
std::map<int, double> map;
std::vector<int> keys;
keys.reserve(map.size());
for(const auto& [key, value] : map) {
keys.push_back(key);
}
// To get the values
std::map<int, double> map;
std::vector<double> values;
values.reserve(map.size());
for(const auto& [key, value] : map) {
values.push_back(value);
}
The best non-sgi, non-boost STL solution is to extend map::iterator like so:
template<class map_type>
class key_iterator : public map_type::iterator
{
public:
typedef typename map_type::iterator map_iterator;
typedef typename map_iterator::value_type::first_type key_type;
key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;
key_type& operator *()
{
return map_type::iterator::operator*().first;
}
};
// helpers to create iterators easier:
template<class map_type>
key_iterator<map_type> key_begin(map_type& m)
{
return key_iterator<map_type>(m.begin());
}
template<class map_type>
key_iterator<map_type> key_end(map_type& m)
{
return key_iterator<map_type>(m.end());
}
and then use them like so:
map<string,int> test;
test["one"] = 1;
test["two"] = 2;
vector<string> keys;
// // method one
// key_iterator<map<string,int> > kb(test.begin());
// key_iterator<map<string,int> > ke(test.end());
// keys.insert(keys.begin(), kb, ke);
// // method two
// keys.insert(keys.begin(),
// key_iterator<map<string,int> >(test.begin()),
// key_iterator<map<string,int> >(test.end()));
// method three (with helpers)
keys.insert(keys.begin(), key_begin(test), key_end(test));
string one = keys[0];
I found the following three lines of code as the easiest way:
// save keys in vector
vector<string> keys;
for (auto & it : m) {
keys.push_back(it.first);
}
It is a shorten version of the first way of this answer.
Using ranges in C++20 you can use std::ranges::copy like this
#include <ranges>
std::map<int,int> mapints;
std::vector<int> vints;
std::ranges::copy(mapints | std::views::keys, std::back_inserter(vints));
if you want values instead of keys
std::ranges::copy(mapints | std::views::values, std::back_inserter(vints));
and if you don't like the pipe syntax
std::ranges::copy(std::views::values(mapints), std::back_inserter(vints));
The following functor retrieves the key set of a map:
#include <vector>
#include <iterator>
#include <algorithm>
template <class _Map>
std::vector<typename _Map::key_type> keyset(const _Map& map)
{
std::vector<typename _Map::key_type> result;
result.reserve(map.size());
std::transform(map.cbegin(), map.cend(), std::back_inserter(result), [](typename _Map::const_reference kvpair) {
return kvpair.first;
});
return result;
}
Bonus: The following functors retrieve the value set of a map:
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>
template <class _Map>
std::vector<typename _Map::mapped_type> valueset(const _Map& map)
{
std::vector<typename _Map::mapped_type> result;
result.reserve(map.size());
std::transform(map.cbegin(), map.cend(), std::back_inserter(result), [](typename _Map::const_reference kvpair) {
return kvpair.second;
});
return result;
}
template <class _Map>
std::vector<std::reference_wrapper<typename _Map::mapped_type>> valueset(_Map& map)
{
std::vector<std::reference_wrapper<typename _Map::mapped_type>> result;
result.reserve(map.size());
std::transform(map.begin(), map.end(), std::back_inserter(result), [](typename _Map::reference kvpair) {
return std::ref(kvpair.second);
});
return result;
}
Usage:
int main()
{
std::map<int, double> map{
{1, 9.0},
{2, 9.9},
{3, 9.99},
{4, 9.999},
};
auto ks = keyset(map);
auto vs = valueset(map);
for (auto& k : ks) std::cout << k << '\n';
std::cout << "------------------\n";
for (auto& v : vs) std::cout << v << '\n';
for (auto& v : vs) v += 100.0;
std::cout << "------------------\n";
for (auto& v : vs) std::cout << v << '\n';
std::cout << "------------------\n";
for (auto& [k, v] : map) std::cout << v << '\n';
return 0;
}
Expected output:
1
2
3
4
------------------
9
9.9
9.99
9.999
------------------
109
109.9
109.99
109.999
------------------
109
109.9
109.99
109.999
With atomic map example
#include <iostream>
#include <map>
#include <vector>
#include <atomic>
using namespace std;
typedef std::atomic<std::uint32_t> atomic_uint32_t;
typedef std::map<int, atomic_uint32_t> atomic_map_t;
int main()
{
atomic_map_t m;
m[4] = 456;
m[2] = 45678;
vector<int> v;
for(map<int,atomic_uint32_t>::iterator it = m.begin(); it != m.end(); ++it) {
v.push_back(it->second);
cout << it->first << " "<<it->second<<"\n";
}
return 0;
}
You can use get_map_keys() from fplus library:
#include<fplus/maps.hpp>
// ...
int main() {
map<string, int32_t> myMap{{"a", 1}, {"b", 2}};
vector<string> keys = fplus::get_map_keys(myMap);
// ...
return 0;
}
With Eric Niebler's range-v3 library, you can take a range and write it out directly to a container using ranges::to (hopefully soon in std, maybe C++26?):
[Demo]
#include <fmt/ranges.h>
#include <map>
#include <range/v3/all.hpp>
int main() {
std::map<int, int> m{ {1, 100}, {2, 200}, {3, 300} };
auto keys{ m | ranges::views::keys | ranges::to<std::vector<int>>() };
fmt::print("{}", keys);
}
// Outputs: [1, 2, 3]
Slightly similar to one of examples here, simplified from std::map usage perspective.
template<class KEY, class VALUE>
std::vector<KEY> getKeys(const std::map<KEY, VALUE>& map)
{
std::vector<KEY> keys(map.size());
for (const auto& it : map)
keys.push_back(it.first);
return keys;
}
Use like this:
auto keys = getKeys(yourMap);
(I'm always wondering why std::map does not include a member function for us to do so.)
Because it can't do it any better than you can do it. If a method's implementation will be no superior to a free function's implementation then in general you should not write a method; you should write a free function.
It's also not immediately clear why it's useful anyway.
I want to store a floating point value for an unordered pair of an integers. I am unable to find any kind of easy to understand tutorials for this. E.g for the unordered pair {i,j} I want to store a floating point value f. How do I insert, store and retrieve values like this?
Simple way to handle unordered int pairs is using std::minmax(i,j) to generate std::pair<int,int>. This way you can implement your storage like this:
std::map<std::pair<int,int>,float> storage;
storage[std::minmax(i,j)] = 0.f;
storage[std::minmax(j,i)] = 1.f; //rewrites storage[(i,j)]
Admittedly proper hashing would give you some extra performance, but there is little harm in postponing this kind of optimization.
Here's some indicative code:
#include <iostream>
#include <unordered_map>
#include <utility>
struct Hasher
{
int operator()(const std::pair<int, int>& p) const
{
return p.first ^ (p.second << 7) ^ (p.second >> 3);
}
};
int main()
{
std::unordered_map<std::pair<int,int>, float, Hasher> m =
{ { {1,3}, 2.3 },
{ {2,3}, 4.234 },
{ {3,5}, -2 },
};
// do a lookup
std::cout << m[std::make_pair(2,3)] << '\n';
// add more data
m[std::make_pair(65,73)] = 1.23;
// output everything (unordered)
for (auto& x : m)
std::cout << x.first.first << ',' << x.first.second
<< ' ' << x.second << '\n';
}
Note that it relies on the convention that you store the unordered pairs with the lower number first (if they're not equal). You might find it convenient to write a support function that takes a pair and returns it in that order, so you can use that function when inserting new values in the map and when using a pair as a key for trying to find a value in the map.
Output:
4.234
3,5 -2
1,3 2.3
65,73 1.23
2,3 4.234
See it on ideone.com. If you want to make a better hash function, just hunt down an implementation of hash_combine (or use boost's) - plenty of questions here on SO explaining how to do that for std::pair<>s.
You implement a type UPair with your requirements and overload ::std::hash (which is the rare occasion that you are allowed to implement something in std).
#include <utility>
#include <unordered_map>
template <typename T>
class UPair {
private:
::std::pair<T,T> p;
public:
UPair(T a, T b) : p(::std::min(a,b),::std::max(a,b)) {
}
UPair(::std::pair<T,T> pair) : p(::std::min(pair.first,pair.second),::std::max(pair.first,pair.second)) {
}
friend bool operator==(UPair const& a, UPair const& b) {
return a.p == b.p;
}
operator ::std::pair<T,T>() const {
return p;
}
};
namespace std {
template <typename T>
struct hash<UPair<T>> {
::std::size_t operator()(UPair<T> const& up) const {
return ::std::hash<::std::size_t>()(
::std::hash<T>()(::std::pair<T,T>(up).first)
) ^
::std::hash<T>()(::std::pair<T,T>(up).second);
// the double hash is there to avoid the likely scenario of having the same value in .first and .second, resulinting in always 0
// that would be a problem for the unordered_map's performance
}
};
}
int main() {
::std::unordered_map<UPair<int>,float> um;
um[UPair<int>(3,7)] = 3.14;
um[UPair<int>(8,7)] = 2.71;
return 10*um[::std::make_pair(7,3)]; // correctly returns 31
}
basically, I have the
map<std::string, int>
so if i have
foo 5
bar 10
jack 3
in the map, I want to display it (notice the reverse order)
bar 10
foo 5
jack 3
And every time it is updated, I want iterate through all the elements, cout them, sorted by value. What is the good way to implement that? should I provide a comparator to the constructor?
I want to note that values in the map will be updated at least 100 million times, so efficiency is crucial, where as extra-space is no problem
Please no Boost solutions...thx
struct keyval_t { std::string key; int val; };
int operator<(const keyval_t &a, const ketval_t &b)
{ return a.val<b.val || (a.val==b.val && a.key<b.key); }
Then you need one map and one set:
map<std::string, int>; set<keyval_t>;
On update, you need to look up the map first to determine the key-value pair and then update both map and set. On printing, you just iterate through the set. In terms of theoretical time complexity, this is optimal. It doubles the memory, though. Does this meet your goal?
To reduce memory, you may consider the following:
map<std::string,uint64_t>; set<uint64_t>;
The value of the map (also the key of the set) is: (uint64_t)val<<32|counter, where counter is something that differentiates identical values. For example, whenever you insert a key, increase the counter by 1. You do not need to update the counter when you update the value. If you do not like uint64_t, use pair<int,int> instead. This solution is also faster as it avoids comparisons between strings.
If you want a performant map sorted by both key and value, you want Boost MultiIndex, it gets updated (resorted) on every update (which you have to do manually) and has a good documentation.
The previous responses have the inconvenience not to take into account the initial requirements (the key is std::string and the value is int).
EDITED: following the comments, I suppose presenting it directly with a Bimap is better :)
So here we go, right in!
#include <boost/bimap.hpp>
class MyMap
{
struct name {};
struct value {};
typedef boost::tagged<name, std::string> tagged_name;
typedef boost::tagged<value, int> tagged_value;
// unordered_set_of: guarantees only unicity (not order)
// multi_set_of: guarantees only order (not unicity)
typedef boost::bimap< boost::unordered_set_of< tagged_name >,
boost::multi_set_of< tagged_value,
std::greater< tagged_value >
>
> impl_type;
public:
// Redefine all usual types here
typedef typename impl_type::map_by<name>::const_iterator const_iterator;
typedef typename impl_type::value_type value_type;
// Define the functions you want
// the bimap will not allow mutators because the elements are used as keys
// so you may want to add wrappers
std::pair< iterator, bool > insert(const value_type & x)
{
std::pair< iterator, bool > result = m_impl.insert(x);
if (result.second) this->display();
return result;
} // insert
iterator insert(iterator position, const value_type & x)
{
iterator result = m_impl.insert(x);
this->display();
return result;
} // insert
template< class InputIterator >
void insert(InputIterator first, InputIterator last)
{
m_impl.insert(first, last);
this->display();
} // insert
private:
void display() const
{
// Yeah I know about std::for_each...
typedef typename impl_type::map_by<value>::const_iterator const_it;
for (const_it it = m_impl.begin(), end = m_impl.end(); it != end; ++it)
{
// Note the inversion of the 'second' and 'first',
// we are looking at it from the right
std::cout << it->second << " " << it->first << std::endl;
}
}
impl_type m_impl;
}; // class MyMap
Here you go.
I strongly suggest that you consult the bimap documentation though. There are a lot of possibilities for storing (set_of, unordered_set_of, unconstrained_set_of, the muli variants, the list_of variant...) so there is probably one that could do what you want.
Then you also have the possibility to just sort each time you display:
#include <set>
#include <map>
// Just use a simple std::map<std::string,int> for your impl_type
// Mutators are allowed since the elements are sorted each time you display
struct Comparator
{
bool operator(const value_type& lhs, const value_type& rhs) const
{
return lhs.second < rhs.value;
}
};
void display() const
{
typedef std::multi_set<value_type, Comparator> sort_type;
sort_type mySet;
std::copy(m_impl.begin(), m_impl.end(), std::inserter(mySet, mySet.end()));
for (sort_type it = mySet.begin(), end = mySet.end(); it != end; ++it)
{
std::cout << it->first<< " " << it->second << std::endl;
}
}
It should be easier to understand my point now :)
Well, you have to sort by key. The easiest way to "sort by value" is to use a multimap with the key and value switched. So here's a way to do that (note -- i don't have access to a compiler right now, so if it doesn't compile, I'm sorry):
#include <algorithm>
#include <functional>
#include <map>
#include <string>
#include <utility>
typedef std::multimap<int, std::string, std::greater<int> > MapType;
struct MapKeyOutput
{
void operator()(const MapType::value_type& val)
{
std::cout << val.second << " " << val.first << "\n";
}
}
void display_insert(MapType& m, const std::string& str, int val)
{
m.insert(std::make_pair(val, str));
std::for_each(m.begin(), m.end(), MapKeyOutput());
}
int main()
{
MapType m;
display_insert(m, "Billy", 5);
display_insert(m, "Johnny", 10);
}
You could also make a map class that uses a multimap internally, I just didn't want to type it out. Then display_insert would be some member function instead. This should demonstrate the point, though. Points of interest:
typedef std::multimap<int, std::string, std::greater<int> > MapType;
Note the comparator is greater<int> to sort descending. We're using a multimap so more than one name can have the same number.
struct MapKeyOutput
{
void operator()(const MapType::value_type& val)
{
std::cout << val.second << " " << val.first << "\n";
}
}
This is a function object to output one element in the map. second is output before first so the order is what you want.
std::for_each(m.begin(), m.end(), MapKeyOutput());
This applies our function object to every element in m.