c++: use map as value of another map - c++

I just wonder if I can use a "complicated" map as the value of another map. I have self-defined several structs as follow:
typedef std::vector<std::string> pattern;
typedef std::map<int, std::vector<pattern>> dimPatternsMap;
typedef std::map<int, dimPatternsMap> supportDimMapMap;
OK let me explain these things...pattern is a vector of strings. For the "smaller" map dimPatternsMap, the key is an integer which is the dimension of pattern (the size of that vector containing strings) and the value is vector containing patterns (which is a vector of vectors...).
The "bigger" map supportDimMapMap also use an integer as the key value, but use dimPatternsMap as its value. The key means "support count".
Now I begin to construct this "complicated" map:
supportDimMapMap currReverseMap;
pattern p = getItFromSomePlace(); //I just omit the process I got pattern and its support
int support = getItFromSomePlaceToo();
if(currReverseMap.find(support) == currReverseMap.end()) {
dimPatternsMap newDpm;
std::vector<pattern> newPatterns;
newPatterns.push_back(currPattern);
newDpm[dim] = newPatterns;
currReverseMap[support] = newDpm;
} else{
dimPatternsMap currDpm = currReverseMap[support];
if(currDpm.find(dim) == currDpm.end()) {
std::vector<pattern> currDimPatterns;
currDimPatterns.push_back(currPattern);
currDpm[dim] = currDimPatterns;
} else {
currDpm[dim].push_back(currPattern);
}
}
Forgive me the code is really a mass...
But then as I want to traverse the map like:
for(supportDimMapMap::iterator iter = currReverseMap.begin(); iter != currReverseMap.end(); ++iter) {
int support = iter->first;
dimPatternsMap dpm = iter->second;
for(dimPatternsMap::iterator ittt = dpm.begin(); ittt != dpm.end(); ++ittt) {
int dim = ittt->first;
std::vector<pattern> patterns = ittt->second;
int s = patterns.size();
}
}
I found the value s is always 1, which means that for each unique support value and for each dimension of that support value, there is only one pattern! But as I debug my code in the map constructing process, I indeed found that the size is not 1 - I actually added the new patterns into the map successfully...But when it comes to traversing, all the sizes become 1 and I don't know why...
Any suggestions or explanations will be greatly appreciated! Thanks!!

dimPatternsMap currDpm = currReverseMap[support];
currDpm is a copy of currReverseMap[support]. It is not the same object. So then when you make changes to currDpm, nothing within currReverseMap changes.
On the other hand, if you use a reference:
dimPatternsMap& currDpm = currReverseMap[support];
then currDpm and currReverseMap[support] really are the same object, so later statements using currDpm will really be changing a value within currReverseMap.
There are a few other places where your code could benefit from references too.

My guess: you should use a reference in your else:
dimPatternsMap& currDpm = currReverseMap[support];
Your current code creates a copy instead of using the original map.

Your problem is this line:
dimPatternsMap currDpm = currReverseMap[support];
Based on the code following it, it wants to read like this:
dimPatternsMap& currDpm = currReverseMap[support];
Without the & you modify a copy of the entry rather than the existing entry.

Your code is making several copies of the objects underneath, try using more references and iterators (find() already gives you an element if it was found, for example).
For example, dimPatternsMap currDpm = currReverseMap[support]; actually makes a copy of a map in your structure and adds an element to it (not to the original). Try using a reference instead.

Related

Changing the Value of a paired item in a Map Data structure having a list of items as a value

I could you some help. I have a unordered map data structure, where each map item (key) is a list of (node,value) pairs. see data structure below.
I am changing the value of a paired item in a list using an iterator, but it's not working.
I am wondering how I can get the value changed.
typedef std::unordered_map< std::string, std::list< std::pair<std::string, int> > > di_Acyclic_Graph_Structure;
di_Acyclic_Graph_Structure global_RT;
global_RT["ialu"].push_back(std::make_pair("ialu1",1));
global_RT["ialu"].push_back(std::make_pair("ialu2",1));
adjNodesStructure adjNodes = global_RT["ialu"];
for (auto iterator=adjNodes.begin(); iterator!=adjNodes.end(); iterator++)
{
if (whatever reason)
{
// changing the value of the second item of the
(*iterator).second = 2;
// I expect the value of global["ialu"] -> which gives me a list ialu1, ialu2
// I expect the value of ialu1 to become 2.
// This is not working
}
}
When you use =, and the left-hand-side is not a reference, you are creating a copy of what's on the right side of the =. Thus what is occurring is that you're working on a copy, and not the original item.
Unlike other popular computer languages such as Java, in C++, the = does not automatically use references. If you are used to such languages like Java, C#, etc. this is where C++ becomes a totally different animal.
C++ is a value-based language, where usage of = will make a copy unless you specifically want a reference.
Thus, what you should be doing is to get a reference to the item you want to change.
To get a reference:
adjNodesStructure& adjNodes = global_RT["ialu"];
Note the usage of the declaration of the reference variable using &. Just that one character change in the line makes all the difference.

What can bring a std::map to not find one of its keys?

I have a std::map associating const char* keys with int values:
std::map<const char*, int> myMap;
I initialize it with three keys, then check if it can find it:
myMap["zero"] = 0;
myMap["first"] = 1;
myMap["second"] = 2;
if (myMap.at("zero") != 0)
{
std::cerr << "We have a problem here..." << std::endl;
}
And nothing is printed. From here, everything looks ok.
But later in my code, without any alteration of this map, I try to find again a key:
int value = myMap.at("zero");
But the at function throws an std::out_of_range exception, which means it cannot find the element. myMap.find("zero") thinks the same, because it returns an iterator on the end of the map.
But the creepiest part is that the key is really in the map, if just before the call to the at function, I print the content of the map like this:
for (auto it = myMap.begin(); it != myMap.end(); it++)
{
std::cout << (*it).first << std::endl;
}
The output is as expected:
zero
first
second
How is it even possible? I don't use any beta-test library or anything supposed to be unstable.
You have a map of pointers to characters, not strings. The map lookup is based on the pointer value (address) and not the value of what's pointed at. In the first case, where "zero" is found in the map, you compiler has performed some string merging and is using one array of characters for both identical strings. This is not required by the language but is a common optimization. In the second case, when the string is not found, this merging has not been done (possibly your code here is in a different source module), so the address being used in the map is different from what was inserted and is then not found.
To fix this either store std::string objects in the map, or specify a comparison in your map declaration to order based on the strings and not the addresses.
key to map is char * . So map comparison function will try to compare raw pointer values and not the c style char string equivalence check. So declare the map having std::string as the key.
if you do not want to deal with the std::string and still want the same functionality with improved time complexity, sophisticated data structure is trie. Look at some implementations like Judy Array.

std::map check if a map has been assigned a non-default value?

Lets say I have a complex map defined as
std::map<int, std::pair< vector<int>, float> > complex_map;
Let us assume I initialized this map as
for (int k=0;k<5;k++)
{
std::pair< vector<int>, float> complex_map_child;
complex_map[k]=complex_map_child;
}
Next, I populate some entries of this map:
float test_1 = .7888;
vector<int> test_2;
test_2.push_back(1);
test_2.push_back(2);
complex_map[1].first = test_2;
complex_map[1].second = test_1;
So corresponding to key value 1 of complex_map, I have the pair of values corresponding to test_1 and test_2.
Now how do I check if I have explicitly added values to the map? i.e in this example how do I say that I havent explicitly filled up say complex_map[0]?
Looks like you are using std::map::operator[] improperly and try to go over it - you are getting object like this:
auto &complex_value = complex_map[0];
and now you try to check if you inserted it before or it was implicitly created by std::map::operator[].
Just do not use std::map::operator[] to access elements. Only use it when you need to set value in a map.
So proper solution would be to use different methods:
// I just want to check if key 0 is there
if( complex_map.count( 0 ) ) {
...
}
// I want to access element by key 0 if it is there
auto it = complex_map.find( 0 );
if( it != complex_map.end() ) {
auto &complex_value = it->second;
...
}
and so on. I know that writing complex_map[0] is shorter but you are creating a problem that trying to solve convoluted way.
Now how do I check if I have explicitly added values to the map? i.e in this example how do I say that I havent explicitly filled up say complex_map[0]?
If by "explicitly" you mean you want to find elements that you wrote to after the initialisation loop that executed complex_map[k]=complex_map_child;, then:
you can compare values in the map to complex_map_child to see if they're equal
you can not detect if a map entry was written to with the same value, or changed then reverted (unless you change the data types to ones that track that themselves, or add some extra bookkeeping outside the map yourself)

Can I get a value from std::map without crashing if the key doesn't exist?

I have C++ code like this:
if(rtstructure.find(varName) != rtstructure.end()) {
rtdef = rtstructure[varName];
}
where rtstructure is std::map with std::string for the key.
This code works but it seems like a waste to make it search twice for the same key. If I omit the if case around the assignment, the program crashes if varName points to a key that doesn't exist.
Can I in a single map operation look up a key in a std::map and get its value if it exists, without crashing if it doesn't exist?
find give you a std::map<>::iterator that holds/point to std::pair<>. The iterator can be saved and reused (given that you did not do anything to invalidate it such as erase).
// i don't know the type of rtstructure so i use auto
// you can replace it to the correct type if C++11 is not available
auto it = rtstructure.find(varName);
if(it != rtstructure.end()) {
rtdef = it->second;
}

How do I insert a element into a std::unordered_map<int, vector<Object*>>

I'm trying to create a hash of arrays of pointers to my object.
The hash key is an int for the type of the object, and the array is a list of the objects to render.
What I'm trying to do is :
unordered_map<int, vector<Object*> > drawQueue;
drawQueue.clear(); // new empty draw queue
for ( ... ) {
drawQueue.at(type).push_back(my_obj);
}
So I'm not familiar enough with the nuances of the STL stuff, since I get an exception saying out_of_bounds, which is what happens when the key doesn't exist.
So I figured I need to create the key first, and then add to the vector :
if (drawQueue.count(type)) {
// key already exists
drawQueue.at(type).push_back(my_obj);
} else {
//key doesn't exist
drawQueue.insert(type, vector<Object*>); // problem here
drawQueue.at(type).push_back(my_obj);
}
But now I'm really lost, as I don't know how to create/initialise/whatever an empty vector to the insert of the unordered_map...
Or am I doing this the entirely wrong way?
You are not using insert in the proper way. This should work:
drawQueue.insert(std::make_pair(type, std::vector<Object*>()));
If using C++11, the previous statement can be simplified to:
drawQueue.emplace(type, std::vector<Object*>());
By using this approach the element is constructed in-place (i.e., no copy or move operations are performed).
I also include links to the documentation for insert and emplace.
I think this is an easy approach. My example will create an unordered_map string as key and integer vector as values.
unordered_map<string,vector<int>> keys;
keys["a"] = vector<int>(); // Initialize key with null vector
keys["a"].push_back(1); // push values into vector.
keys["a"].push_back(5);
for(int i : keys["a"] ){
cout << i << "\t";
}
I think you could simplify it by
drawQueue[type].push_back(my_obj);
The operator [] would do the insert for you if the key is not found.