简体   繁体   中英

How to use std::map to retrive a std::vector<int> in c++?

What I'm trying to do is to somehow replicate in c++ the structure and functionality of a Perl Hash. For those not familiar with Perl, in such language a key can be used to point not only to a variable but other more complicated structures like arrays or vector or even other hashes. What I have tried so far is to create a vector of size 10 which is going to be my mapped_type

size_t size = 10;
std::vector<int> v1(size);
... Code that fills the v1...

and then create the map with v1 and fill it with values.

std::map<unsigned int, v1> x;
std::map<unsigned int,std::vector<int>>::iterator p=x.find(key);
if(p==m.end()) m[key]=v1;

Later, I plan to loop through all the keys and retrieve the vectors associated with those keys

for (std::map<unsigned int, std::vector<int>>::iterator p=x.begin(); p!=x.end(); ++p) {
  ...Do something with the p...
}

but of course these two last piece of code does not work at all.

I have successfully created other iterators like

std::map<unsigned int, unsigned int> x;
std::map<unsigned int, unsigned int>::iterator p=x.find(key);
if(p==m.end()) m[key]=1;
for (std::map<unsigned int, unsigned int>::iterator p=x.begin(); p!=x.end(); ++p) {
 ...Do something with the p...
}

but the mapped type is just a variable containing a single number or character. I want to be able to call and work with a complete vector using map (or any other C++ functionality). Is there a way to do this?

The problem is with the line:

std::map<unsigned int, v1> x;

You can't use v1 as the mapped type as v1 is not a type, it is an instance of a type. You can either write the type explicitly:

std::map<unsigned int, std::vector<int>> x;

Or use decltype :

std::map<unsigned int, decltype(v1)> x;

Also, if you want a hash map then you should use std::unordered_map rather than std::map , which is actually a red-black tree

With C++11 you might do something like this:

#include <iostream>
#include <map>
#include <vector>

int main() {
    // type helpers
    using MyVec = std::vector<int>;
    using MyMap = std::map<unsigned int, MyVec>;

    // create v1
    MyVec v1 { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
    // Or MyVec v1; v1.resize(10);

    // create the map
    MyMap x;

    unsigned int key = 123;
    auto it = x.find(key);
    if (it == x.end())
        x[key] = v1;  // causes a COPY of the entire vector

    for (const auto& idx: x) {
        // idx.first is the key
        // idx.second is the vector
        std::cout << idx.first << ": ";
        for (auto val: idx.second) {
            // ^ no &, makes copies of values but they're ints so it's ok.
            std::cout << val << " ";
        }
        std::cout << "\n";
    }
}

Live demo: http://ideone.com/1r13zB

This give some clues as to how to iterate through a map. Using p->second gives you complete access to the int vector, and p->first gives you complete access to the map's key.

std::map<unsigned int, v1> x; // v1 -> typo?
std::map<unsigned int, std::vector<int> > x; // does this work?

I'm not sure if you're copying your code directly or providing pseudo code, but when you nest template arguments you have to insert a space into the final right angle brackets since >> is a C++ operator. I would imagine you would have found this out by getting a build error though.

I often use similar (nested) structures and i know this approach work, so consider the following, if this answers your question. Your example is a map with int keys and vector values.

std::map< unsigned int, vector<int> > myMap;

  // fill some vectors and put them in the myMap

  for (int i = 0 ; i < 10 ; i++){

    vector<int> myValueHolder;

    for (int j = 0 ; j < 10 ; j++){

      myValueHolder.push_back(10*i + j);

    }

    myMap[i] = myValueHolder;

  }
  // vectors are filled and pushed into the map with int keys
  // retrieve vectors 

  for (std::map<unsigned int, vector<int> >::iterator iter = myMap.begin() ; iter != myMap.end() ; iter++ ){

    unsigned int currentKey = iter->first; // first gets the key in the 
    vector<int> currentValue = iter->second; // second get the value

    cout << "key is: " << currentKey << endl << "values are:" << endl;;
    for (unsigned i = 0 ; i < currentValue.size() ; i++){ cout << currentValue.at(i) << endl; }

  }

  // or using any of the keys 

  vector<int> someRandomVectorFromTheMap = myMap[5];

  for (unsigned i = 0 ; i < someRandomVectorFromTheMap.size() ; i++){ cout << someRandomVectorFromTheMap.at(i) << endl; }

I tested this and works as expected, hope answers what you ask. And i compiled it with this flag COMPILE_AS_Cpp0X = $(-std=c++14) in case you wonder if it's 14 compatible

EDIT : I forgot to mention that what you wrote suggests you are not very familiar with the maps and their iterator methods first and second. This methods are very useful when iterating over map elements. Also usage of .find()

vector<int> someVectorTofind = myMap.find(8)

OR if you want to check if such key exists

vector<int> holder;
if (myMap.find(15) != myMap.end()) { holder = myMap.at(15)}; 

Check more map and vector examples is my suggestion

If you try to define the map with type of v1, you need decltype()

#include <string>
#include <vector>
#include <map>

int main( void )
{
    size_t size = 10; 
    std::vector<int> v1(size);
    std::map<unsigned int, decltype(v1) > x;
    return 0;
}

Now I'm unsure what you're trying to do with a map<unsigned int, vector int> but the most likely case is that you really want a multimap<unsigned int, int> . With this you could do something like:

while(it != x.cend()) {
    auto end = x.upper_bound(it->first);

    cout << it->first << "\n\t";
    for_each(it, end, [](const auto& i){ cout << i.second << '\t'; });
    cout << endl;
    it = end;
}

Live Example

Which would print your key on one line and an indented tab delineated list of the values associated with the key. There are lots of other clever things you can do with multimap , you can read about the functionality here: http://en.cppreference.com/w/cpp/container/multimap

One thing about your question, you allude to Perl's weak typing. Where you can assign an int to a variable and then turn around and assign a number to it. That's not possible in C++, because it's strongly typed: https://en.wikipedia.org/wiki/Strong_and_weak_typing

What you can do, and what I do in my example, is to assign a char 's value to an the int value of the multimap . This assignment will cast the integral value of the character to an int . If you want the character back all you'll need to do is static_cast it back to a char .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM