Obtaining list of keys and values from unordered_map - c++

What is the most efficient way of obtaining lists (as a vector) of the keys and values from an unordered_map?
For concreteness, suppose the map in question is a unordered_map<string, double>.
I'd then like to obtain the keys as a vector<string>, and the values as a vector<double>.
unordered_map<string, double> um;
vector<string> vs = um.enum_keys();
vector<double> vd = um.enum_values();
I can just iterate across the map and collect the result, but is there a more
efficient method? It would be nice to have a method that also works for regular map,
since I might switch to that.

Okay, here you go:
std::vector<Key> keys;
keys.reserve(map.size());
std::vector<Val> vals;
vals.reserve(map.size());
for(auto kv : map) {
keys.push_back(kv.first);
vals.push_back(kv.second);
}
Efficiency can probably be improved, but there it is. You're operating on two containers though, so there's not really any STL magic that can hide that fact.
As Louis said, this will work for any of the STL map or set containers.

Using C++-14 you could also do the following (edited to contain full source):
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;
typedef string Key;
typedef int Value;
auto key_selector = [](auto pair){return pair.first;};
auto value_selector = [](auto pair){return pair.second;};
int main(int argc, char** argv) {
// Create a test map
unordered_map<Key, Value> map;
map["Eight"] = 8;
map["Ten"] = 10;
map["Eleven"] = 11;
// Vectors to hold keys and values
vector<Key> keys(map.size());
vector<Value> values(map.size());
// This is the crucial bit: Transform map to list of keys (or values)
transform(map.begin(), map.end(), keys.begin(), key_selector);
transform(map.begin(), map.end(), values.begin(), value_selector);
// Make sure this worked: Print out vectors
for (Key key : keys) cout << "Key: " << key << endl;
for (Value value : values) cout << "Value: " << value << endl;
return 0;
}
I compiled this with the following command:
g++ keyval.cpp -std=c++14 -o keyval
Testing it printed the keys and values as expected.

In STL there is no built-in method to get all keys or values from a map.
There is no different to iterate a unordered map or regular map, the best way is to iterate it and collect key or value to a vector.
You can write a template function to iterate any kind of map.

Joining late, but thought this might be helpful to someone.
Two template functions making use of key_type and mapped_type.
namespace mapExt
{
template<typename myMap>
std::vector<typename myMap::key_type> Keys(const myMap& m)
{
std::vector<typename myMap::key_type> r;
r.reserve(m.size());
for (const auto&kvp : m)
{
r.push_back(kvp.first);
}
return r;
}
template<typename myMap>
std::vector<typename myMap::mapped_type> Values(const myMap& m)
{
std::vector<typename myMap::mapped_type> r;
r.reserve(m.size());
for (const auto&kvp : m)
{
r.push_back(kvp.second);
}
return r;
}
}
Usage:
std::map<long, char> mO;
std::unordered_map<long, char> mU;
// set up the maps
std::vector<long> kO = mapExt::Keys(mO);
std::vector<long> kU = mapExt::Keys(mU);
std::vector<char> vO = mapExt::Values(mO);
std::vector<char> vU = mapExt::Values(mU);

Similar to #Keith Layne answer, but reserve is done in one line;
And the for loop is using reference (instead of copying it by value each entry) and making it const. But if C++14 can be used then #Marius Renn answer should be better.
std::map<long, char> mO;
// populate the mO
std::vector<long> keys(mO.size());
std::vector<char> vals(mO.size());
for(const auto &kv : mO) {
keys.push_back(kv.first);
vals.push_back(kv.second);
}

I'm using this with the range-v3 library, it will be soon in the STL library too, with a little luck in c++23 (ranges::to definitely and views::key maybe).
std::unordered_map<std::string, int> map {{"one", 1}, {"two", 2}, {"three", 3}};
auto keys = map | ranges::views::keys | ranges::to<std::vector>();

Related

How to sort map using comparator with reflection in original map?

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 . . .

why map's key can't be inserted into a vector by iterator? [duplicate]

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.

Delete all not found i.e. delete all key/values in map not found in set

I tried but failed to get the following working with std::algorithms:
I have a a std::map<key_t,value_t> cache and a std::set<key_t> selected_items and I want to delete key/value pairs from cache, except for keys contained in selected_items.
Here's what I wrote without algorithms:
//This could really be written better with std::algorithms but time...
//Delete old
for (auto pair = cache.begin(); pair != cache.end(); ) {
if (selected_items.find(pair->first) == selected_items.end())
pair = cache.erase(pair);
else
++pair;
}
To make use of the algorithms library, I figured I need to use std::set_difference with a compare function and either std::remove or std::map::erase. But I can't connect the pieces, failed at:
What's the correct compare function?
Do I have to generate a temporary set with the keys that should be deleted, or can I use the output iterator directly for the remove/erase?
How should my code look?
This is actually a very interesting question! It turns out that there are several difficulties involved...
std::map uses a std::pair<const Key, T> which makes copying/moving of the std::pairs impossible (note the const)
no algorithm can perform an actual call to std::map<>::erase()because it would invalidate the current iterator
a standard way to reorder the elements in cache (with a e.g simple call to std::partition) and then to drop the last elements in cache cannot work due to point 1
Therefore you have two possibilities:
Build your own loop that calls erase appropriately
Use <algorithm> and a second map that stores the results
Since you are only interested in the second option, we can examine e.g. the use of std::set_difference() which indeed does exactly what you want.
However since the iterators of the std::map and the std::set point to different kinds of objects (std::pair and Key), we have to be careful with our Comparator.
A naive approach is simply to supply a function that takes a const std::pair & and a const Key &. But this does not work on my machine! (I do not know if this is a bug... Mac OS X 10.10.5) because std::set_difference() decides to sometimes call the Comparator with the arguments in reversed order...
Long story short, here is a solution featuring SFINAE and std::set_difference():
#include <map>
#include <set>
#include <iterator>
#include <algorithm>
using Key = int;
using Value = char;
using Pair = std::map<Key,Value>::value_type;
struct Comparator
{
// Maybe use a custom comparator instead of '<' (see std::set documentation)
template<class P, class K> auto operator()( const P &p, const K &k ) -> decltype(p.first < k)
{ return (p.first < k); }
template<class P, class K> auto operator()( const K &k, const P &p ) -> decltype(k < p.first)
{ return (k < p.first); }
};
int main( void )
{
std::map<Key,Value> cache = { {1, 'a'}, {2, 'b'}, {3, 'c'}, {4, 'd'} };
std::set<Key> selected_items = { 2, 4 };
std::map<Key,Value> new_cache;
std::set_difference( cache.begin(), cache.end(),
selected_items.begin(), selected_items.end(),
std::inserter( new_cache, new_cache.end() ),
Comparator() );
cache = std::move( new_cache ); // Don't use new_cache from here on
return 0;
}
If both your containers use the same sort order on key_t, you can just walk both containers and remove element in one container if it is not in the other without having to search for it. O(N) complexity.
Unfortunately, none of the standard algorithms can do removals for you because they work on iterators. To remove using an iterator its container object is required.
Naive attempt:
#include <iostream>
#include <map>
#include <set>
int main() {
std::map<int, int> m = {{1, 2}, {3, 4}, {4, 5}};
std::set<int> s = {1, 3, 5};
for (auto it = begin(m); it != end(m); ){
if (s.count(it->first))
m.erase(it++);
else
++it;
}
for (auto &e : m){
std::cout << e.first << ' ' << e.second << '\n';
}
}
Incorporating #MaximEgorushkin's idea:
#include <iostream>
#include <map>
#include <set>
void erase_elements_from_map_that_are_not_in_set(
std::map<int, int> &m, std::set<int> &s){
auto sit = begin(s);
for (auto it = begin(m); it != end(m) && sit != end(s); ){
while (*sit < it->first){
++sit;
if (sit == end(s))
return;
}
if (*sit == it->first)
m.erase(it++);
else
++it;
}
}
int main() {
std::map<int, int> m = {{1, 2}, {3, 4}, {4, 5}};
std::set<int> s = {1, 3, 5};
erase_elements_from_map_that_are_not_in_set(m, s);
for (auto &e : m){
std::cout << e.first << ' ' << e.second << '\n';
}
}
You may need to replace < in the while loop with the common comparison function of the s and m.
Concerning STL algorithms, you can use std::find instead of count in the first example, but that is just awkward. I don't know of any other way to use STL algorithms here and I don't think they are needed. You could put the code in your own erase_elements_from_map_that_are_not_in_set function if you frequently need it.
This sounds like a case for the erase-remove idiom:
typedef std::map<int,std::string> cache_t;
typedef std::set<cache_t::key_type> set_t;
void update_cache(cache_t& cache, const set_t& selected_items)
{
auto test = [selected_items](const cache_t::value_type& x){
return selected_items.find(x.first) == selected_items.end();
};
cache.erase(std::remove_if(cache.begin(), cache.end(), test), cache.end());
}
But this isn't possible here, as the error message indicates:
32883794.cc:16:64: required from here
/usr/include/c++/4.8/bits/stl_pair.h:170:8: error: assignment of read-only member ‘std::pair<const int, std::basic_string<char> >::first’
first = std::forward<first_type>(__p.first);
The problem is that we obtain only iterator of pair<const int key_t, value_t> from the map, so can't move its elements.
It should be possible to use std::copy_if to create a new instance of cache but that likely has substantial memory overhead compared to the approach using a loop.

How can i sort a map by its .second parameter [duplicate]

This question already has answers here:
How can I sort an STL map by value?
(11 answers)
Closed 6 years ago.
If i have a stl map from string to int and i want to print all the int values sorted - how can i do that?
You cannot sort a map by its values due to the implementation of the map.
If you want to emit the elements in the map in such a sorted order then you have to first dump the map contents into a vector (say) and sort that vector:
template <typename T1, typename T2>
struct less_second {
typedef pair<T1, T2> type;
bool operator ()(type const& a, type const& b) const {
return a.second < b.second;
}
};
map<string, int> mymap;
// …
vector<pair<string, int> > mapcopy(mymap.begin(), mymap.end());
sort(mapcopy.begin(), mapcopy.end(), less_second<string, int>());
Or alternatively, just copy the values from the map, leaving the keys, and sort the resulting vector directly.
You can copy all the values into vector and sort it.
#include <algorithm>
#include <map>
#include <vector>
int get_second( pair<string, int> i ){ return i.second; }
int main(int argc, char* argv){
map<string, int> m;
m["tt"] = 2;
m["rr"] = 1;
m["ee"] = 3;
vector<int> v( m.size() );
transform( m.begin(), m.end(), v.begin(), get_second );
sort( v.begin(), v.end() );
for (int i=0; i<v.size(); i++) cout << v[i] << endl;
}
You cannot do this automatically. std::map uses first value (nomen omen 'key') to sort content.
Instead, you can use boost::multi_index_container.
If you need to do this multiple times, it might be more efficient to keep two separate containers, e.g. your map and a sorted container like set or multiset for storing the sorted ints, rather than having to create a container and sort it on the fly. But then you have to keep them synchronized, which could get mucky. You could encapsulate that by wrapping them in a class, or better yet use a boost::multi_index_container.
You cannot sort a map, it's an associative container, not a sequential, and associated containers are sorted by some internal order.
If you want to only print the int values, you could put them into a std::vector, sort the vector, and print the values.
Instead of using a vector, I'd rather just copy them to a set<int>:
#include <map>
#include <set>
#include <string>
#include <iostream>
#include <iterator>
using namespace std;
set<int> map2set(map<string, int> const& m) {
set<int> r;
for (map<string, int>::const_iterator b = m.begin(), e = m.end(); b != e; ++b)
r.insert(b->second);
return r;
}
int main() {
map<string, int> m;
m.insert(make_pair("hello", 42));
m.insert(make_pair("world", 24));
set<int> s = map2set(m);
copy(s.begin(), s.end(), ostream_iterator<int>(cout, "\n"));
}

How to retrieve all keys (or values) from a std::map and put them into a vector?

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.