Storing std map in map
64,411
Solution 1
Try:
std::map< std::string, std::map<std::string, std::string> > someStorage;
someStorage["Hi"]["This Is Layer Two"] = "Value";
Solution 2
someStorage["key"].insert(std::make_pair("key2", "value2")));
If you still wanted to use insert on the outer map as well, here is one way to do it
std::map<std::string, std::string> inner;
inner.insert(std::make_pair("key2", "value2"));
someStorage.insert(std::make_pair("key", inner));
Solution 3
A map has a insert method that accepts a key/value pair. Your key is of type string, so that's no problem, but your value is not of type pair (which you generate) but of type map. So you either need to store a complete map as your value or you change the initial map definition to accept a pair as value.
Solution 4
//Try this:
std::map< std::string, std::map<std::string, std::string> > myMap;
myMap["key one"]["Key Two"] = "Value";
myMap["Hello"]["my name is"] = "Value";
//To print the map:
for( map<string,map<string,string> >::const_iterator ptr=myMap.begin();ptr!=myMap.end(); ptr++) {
cout << ptr->first << "\n";
for( map<string,string>::const_iterator eptr=ptr->second.begin();eptr!=ptr->second.end(); eptr++){
cout << eptr->first << " " << eptr->second << endl;
}
}
Solution 5
Also you can use list initialization:
someStorage.insert( std::make_pair("key", std::map<std::string, std::string> {std::make_pair("key2", "value2")}) );
Author by
Hanut
Updated on May 01, 2020Comments
-
Hanut about 4 years
I have to store std::map as value in std::map
std::map< std::string, std::map<std::string, std::string> > someStorage;
How to insert into second(inner) map? I tried with:
someStorage.insert( std::make_pair("key", std::make_pair("key2", "value2")) );
But this throws a lot of errors. What's wrong?
-
TToni over 13 yearsThat alone won't cut it. Where is the map in the value created?
-
Dark Falcon over 13 yearsYou do realize that someStorage["key"] does an insert if required, using a default-initialized value? (in other words, an empty map is associated with the key if the key does not exist)
-
Admin over 13 yearsThat is needlessly EXPENSIVE as it creates a default string then reassigns it to the value passed in. Best approach is how Dark Falcon has done it: stackoverflow.com/questions/4479017/storing-std-map-in-map/… - I think you're getting voted-up these days only because of your accrued points and not the quality of your answers. I wish people would take the time to read/think about your answers.
-
Martin York over 13 yearsI like my answer because it is the easiest to read. Expense is rarely a factor, maintainability is a real cost. This code is the easiest to maintain. PS. Also copying a string is about as expensive as copying a couple of integers (as
most
std::string implementations uses a copy on write approach and thus the string is not actually copied). -
Matthieu M. over 13 years@Oxsnarder: What is the cost of creating a default string and then assigning a
char const*
to it ? The defaultstd::string
probably performance no memory allocation (if yours does, shoot off your library provider), and then there isstd::string& std::string::operator=(char const*)
, and of course if the string already exists, then you avoid building a temporarystd::string
that won't get inserted anyway... I think that you're being needlessly snappy these days. -
Martin York over 13 yearsYou should not use make_pair. You are assuming that the implementation uses pair. Use
std::map<Key, Value>::value_type
it is typedefed for exactly that reason. -
Dark Falcon over 13 yearsOn the contrary: The C++0x standard says: "For a map<Key,T> the key_type is Key and the value_type is pair<const Key,T>." (23.4.1.2)
-
Stuart Golodetz over 13 yearsI'm not particularly interested in the fight going on here -- but bear in mind that your answer above is not semantically equivalent to
insert
in that it will overwrite a value which is already there whereasinsert
will not. Which is preferable depends on context, of course -- but the original question in this case does seem to ask forinsert
. -
Stuart Golodetz over 13 years@Martin: Indeed -- unlike the case in point, what I said and what you said are semantically equivalent :)
-
Martin York over 13 years@Dark Falcon: Yes exactly. Which is why you should use value_type and not make_pair.
-
Stuart Golodetz over 13 years@Martin: The bit he quoted is in essence a specific guarantee from the standard that the two are equivalent. In other words,
make_pair
is always guaranteed to work here (and for what it's worth, it's the idiomatic usage when doing aninsert
in my experience). -
Martin York over 13 years@Stuart Golodetz: @Dark Falcon: Yes. I understand that. But what do you think the type returned by make_pair is? As a hint its not the same as value_type because "key1", "key2", "value2" are not std::strings! You are lucking out on a couple of implicit conversions. By using value_type you are getting the correct type without having to worry about the compiler doing any implicit conversions. In my team (probably company wide): make_pair is removed at code review time. Here value_type is idiomatic (because it is the type stored in the map).
-
Martin York over 13 years@Stuart Golodetz: To rephrase: insert() will not change the value if it already exists, while the above code will change the value. (As translated from PhDish into English for the rest of us). :-)
-
Stuart Golodetz over 13 years@Martin: Touché ;) I guess the tricky bit was "is not semantically equivalent to", which translates to "does not have the same meaning as". Apologies for talking in weird PhD tongues :-)
-
Stuart Golodetz over 13 years@Martin: I'm aware that there are implicit conversions going on -- they're desirable from my perspective, because code with
make_pair
can often be more readable. If they're causing you a performance problem (you profiled, I assume), then sure, it's fair enough to usevalue_type
. But otherwise I don't see a major benefit. Suppose your map isstd::map<VeryLongKeyTypeName,VeryLongValueTypeName> m;
, you'd certainly want to avoid writingm.insert(std::map<VLKTN,VLVTN>::value_type(k,v));
when you could writem.insert(std::make_pair(k,v));
-- the latter's much clearer. -
Stuart Golodetz over 13 yearsI accept that you could use a
typedef
, but that's extra hassle for what is in most cases little to no benefit. And if your map type depends on template parameters, then you end up having to addtypename
beforestd::map<K,V>::value_type
each time. You could dotypedef typename std::map<K,V>::value_type MapVal;
and thenm.insert(MapVal(k,v));
, sure, but I don't see the real advantage. -
Martin York over 13 yearsI would have done:
typedef std::map<VeryLongKeyTypeName,VeryLongValueTypeName> Storage;
Then itsm.insert(Storage::value_type(key,value));
Which is much cleaner and more readable. The cost of the implicit conversions is not the point (I am sure the compiler optimizations would make it practically the same). The implicit conversion is problematic as it is not always obvious what is happening (you happen to get lucky in the std::string is very well tested in this regards, any other user defined type I would be much more suspicious of what is happening (and thus need to spend time checking)). -
Martin York over 13 yearsThe problem here is not when you first write it. Because you know how all your code is working. But the problem comes from maintaining the code. If any types are changed (and this happens a lot) the implicit conversions can start causing massive problems. For Example Key1 is change from a std::string into Application::Key1 this type change will cascade through the program. Using the correct type the first time will prevent problems. Using make_pair with all the associated (extra type conversions (there will always be some but try and minimiz)) will require a lot more verification.
-
Stuart Golodetz over 13 years@Martin: If e.g.
VeryLongKeyTypeName
was a template parameter, it would end up beingtypename Storage::value_type(key, value)
of course, becausevalue_type
would still be a dependent type. (Just deciding how to reply to the rest, bear with me... :)) -
Stuart Golodetz over 13 yearsAs far as implicit conversions go, it strikes me that the true problem lies with people who allow implicit conversions that they shouldn't. By default, I'd tend to make most single-parameter constructors
explicit
unless I was specifically intending to allow the implicit conversion. (I'd also aim to avoid unwise conversion operators, e.g. providingoperator const char*
for a string type.) If only sensible implicit conversions are supported, I don't see why there should be a major problem. -
Stuart Golodetz over 13 yearsI think the bottom line is that I don't think it makes a vast amount of difference -- the
value_type
way potentially involves an otherwise unneededtypedef
, and is a bit more hassle, but makes the types involved more explicit. Themake_pair
way is a bit more convenient. If used properly, I don't think either should cause real problems. (I have changed key types of maps before, and I've never had a problem with implicit conversions.) YMMV - I guess I wouldn't have a major problem with usingvalue_type
if I worked in one of your teams, but I don't think it really buys you very much. -
Martin York over 13 years@Stuart Golodetz: Well though out arguments. Good luck with the viva.
-
Stuart Golodetz over 13 years@Martin: Removed, sorry -- no offence intended. Was just curious when you said was all.
-
Martin York about 10 years@Gracchus: The performance hit was negligible at best with C++03. With C++11 and move semantics its even less relvant. The thing you need to think about now is weather to use
std::map
orstd::unordered_map
. -
Abhay Hegde almost 8 years@DarkFalcon , How would one construct a map consisting of both primitive values and map inside a map, some thing like the below { 'Key1': '1', 'Key2': { 'a' : '2', 'b' : '3', 'c' : { 'd' : '3', 'e' : '1' } } }