Obtaining list of keys and values from unordered_map

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.

121431 次浏览

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.

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.

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>();