83

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.

Xeo
  • 129,499
  • 52
  • 291
  • 397
Faheem Mitha
  • 6,096
  • 7
  • 48
  • 83
  • 2
    Looking at the draft standard, I don't see an easy way to get what you want, but I may be missing something. You could say `std::vector> v(map.begin(), map.end()); ` which should give you a vector of key-value pairs. – Keith Layne Dec 13 '11 at 03:34
  • @keith.layne: I'm looking for separate vectors for keys and values. – Faheem Mitha Dec 13 '11 at 03:40
  • As I said, there's nothing built-in for that. See below. – Keith Layne Dec 13 '11 at 03:42
  • 1
    @muntoo: Not sure what is with the edit. What is `vector vs = um.enum_keys();` supposed to signify? – Faheem Mitha Dec 13 '11 at 04:22
  • Boost has a transform iterator which can pull just keys or just values, but its faster to do both at once as Keith suggests – Mooing Duck Dec 13 '11 at 05:10
  • @FaheemMitha Just making it 'better'. If you don't like it, you can remove it. `enum_keys()` doesn't exist, but that is the 'format' you want it in, so I thought I'd clarify. – Mateen Ulhaq Dec 13 '11 at 05:44
  • @MooingDuck: Feel free to add a answer using transform. I think it would be useful in cases when someone just wants one of these. – Faheem Mitha Dec 13 '11 at 07:46
  • @FaheemMitha: It turns out to not be a good answer to your particular situation since you want both, but it's described and shown in other questions: http://stackoverflow.com/a/2794168/845092 – Mooing Duck Dec 13 '11 at 17:03
  • @MooingDuck: You could still add an answer, but whatever. The interesting question is whether these fancy methods are faster than the regular ones. I guess one could time them and find out. – Faheem Mitha Dec 13 '11 at 17:09
  • @FaheemMitha: The boost answer isn't faster than keith.layne's answer. It's mostly useful if you want to return an iterator to just keys, or just values for a `iter get_keys()` function. – Mooing Duck Dec 13 '11 at 17:17

6 Answers6

85

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.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
Keith Layne
  • 3,688
  • 1
  • 24
  • 28
  • 2
    Ok, sO I guess there is nothing better than iterating over the map then. I don't recognize the syntax you are using. What does `(auto kv : map)` denote. I would have expected just an iteration (i.e. for loop) over the elements of the map. – Faheem Mitha Dec 13 '11 at 04:01
  • 1
    @FaheemMitha That is the new C++11 for loop. It does exactly what it looks like it does, and combined with `auto` makes things a little more tidy. `auto` saves you from having to explicitly write the type of kv. There are several ways to accomplish essentially the same thing including a for loop over the iterators, `for_each` with a lambda, etc. Since you mentioned `unordered_map` I assumed you were using C++11. – Keith Layne Dec 13 '11 at 04:17
  • I guess I am, but I'm not really familar with the new standard. Thanks. – Faheem Mitha Dec 13 '11 at 04:19
  • 2
    Effienency can be improved with a reserve, but not much will be faster or easier than this. – Mooing Duck Dec 13 '11 at 05:09
  • 5
    I would use `auto&` instead of `auto` here to avoid unnecessary copies of the pair (and underlying first/second members if they are structs). And it won't work for `set` containers because their `value_type` is not a `std::pair` but the Key/Val type itself. – Juicebox Mar 30 '17 at 12:15
  • 1
    instead of `std::vector keys; keys.reserve(map.size());` you can write `std::vector keys(map.size());` same with the other vector – fire in the hole Jan 15 '20 at 09:11
23

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.

Faheem Mitha
  • 6,096
  • 7
  • 48
  • 83
Marius Renn
  • 391
  • 2
  • 5
  • Can you explain this more – Whitecat Nov 19 '15 at 02:25
  • Could you write a self-contained example that can be compiled? Also, if you could mention what compiler to use and provide a command line to use to compile it, that would be helpful. thanks. – Faheem Mitha Nov 19 '15 at 09:13
  • Also, what are `Key` and `Value` and `um` here? You haven't defined them. Perhaps you are using the definition from the question, namely "unordered_map um;", but in that case, you should mention that here again, regardless. – Faheem Mitha Nov 19 '15 at 09:34
  • Sorry folks, was on the bus when I wrote this and stayed rather brief. I now updated the code to something that should compile. Let me know if it does not work. The way this works is that we apply a lambda function to each element of the map (which is a key-value pair). The key-selector simply returns the first element of the pair, while the value selector returns the second. We use the transform function to copy the keys (or values) to the vectors. I am not an expert in std library's algorithms so there may be a more elegant way. – Marius Renn Nov 25 '15 at 05:19
  • 1
    Works here with clang 3.5 and gcc 4.9. Has this any efficiency advantages relative to a plain loop? – Faheem Mitha Nov 26 '15 at 16:47
  • Probably not very efficient due to use of a lambda function, but very elegant as a functional algorithm. – minexew Feb 03 '16 at 19:23
  • 2
    The lambas should introduce no overhead they (like any other functors) can be inlined into the `transform` loop. But when using `auto` be sure to remember it deduces value types, so those range based for loops introduce some copies. You can use `const auto&` instead of plain `auto`, where it makes sense to. – Paul Rooney Jun 04 '16 at 13:26
  • Today's compilers will almost certainly inline the lambda without even requiring the keyword. There will be no performance hit. – John Phu Nguyen May 30 '17 at 23:53
4

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.

Louis
  • 407
  • 2
  • 7
1

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);
elimad
  • 1,132
  • 1
  • 15
  • 27
0

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);  
} 
Anton Krug
  • 1,555
  • 2
  • 19
  • 32
0

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>();
Silver Zachara
  • 2,901
  • 2
  • 16
  • 22