Using map containing set as a value - c++

Basically I have,
typedef map<std::string, set<double> > MAP_STRING_TO_SET;
What is the best way to update (add or remove value) the set with a new value without causing the set to be copied?
The only viable solution I see is to use map<std::string, set<double>* > -- something I don't want to do.
Thanks

The set is only copied in initialization.
You are allowed to do something like
myMap[myString].insert(myDouble);
since map::operator[] returns a reference.

You can also do this:
map<std::string, set<double> >::iterator iter = myMap.find(myString);
if(iter != myMap.end())
{
iter->second.insert(myDouble);
}

I think you can just use swap - eg.
my_map["123"].swap(my_set)
provided clobbering my_set doesn't matter to you. This would swap the previous contents of my_map["123"] with my_set, and it's fast.
The indexing operator[] returns a reference to the contained set - therefore, you can manipulate it just like any other variable. If you want to add/remove individual values, you can just use insert() or erase() methods - eg. my_map["123"].insert(123).
The copying actually takes place when you assign a new set to the map - eg.
my_map["123"]=my_set
would create a copy of my_set. If you don't need to use the value of my_set later, you can use the swap() method, which will just shuffle pointers of the two sets. my_set will, however, have the contents of the previous my_map["123"].

Related

std::unordered_set::find - construct an instance only for find()

A lot of times I see my key is actually inside my value.
For example:
struct Elem {
int key;
// ... Other variables ...
}
That makes me want to use std::unordered_set instead of std::unordered_map, because I already have the key stored inside my value - no need to waste more place for std::unordered_map's .first (key).
Then I start implementing with std::unordered_set and get to the place I need to perform a find() over my std::unordered_set.
Then I realize I need to create an empty-shell Elem so I would be able to find(), beacuse std::unordered_set::find gets a Key for input
template < class Key, // unordered_set::key_type/value_type
class Hash = hash<Key>, // unordered_set::hasher
class Pred = equal_to<Key>, // unordered_set::key_equal
class Alloc = allocator<Key> // unordered_set::allocator_type
> class unordered_set;
Sometimes building an empty-shell Elem is hard / wasteful / maybe even not possible?
For example, when my key/value is
An iterator
A reference
A class with specific c'tor (not constructing the instance only with the key)
Q. Am I missing something?
Q. Is there a way to do find() that isn't wasteful? I mean that doesn't make me create an instance I didn't want to
Something really strange to me - that I already should have the element I'm looking for in order to find it, or at least an empty-shell of it.
When choosing a data structure to hold your data you need to consider your use case.
If you want to look up data from a key you should use a map. If you just want to store unique values in a collection and you don't need to look them up use set.
I don't see why its so much trouble to insert a element as map.emplace_back(elem.key, elem) vs set.emplace_back(elem) if it means that down the road you can just query the elem as map.at(key) or map[key] vs having create an empty elem.
Besides, std::set does the whole key thingamajig (roughly) underwater anyway. (source: What is the difference between set vs map in C++?)

Insertion in maps in C++

map<string ,vector<string> > hashes;
hashes.insert(pair<string,vector<string> > (a,b )); //error coming
What is the problem coming when i am using the above statement in C++, where a and b are strings?
How does the insertion takes place in this type (i.e. one container containing more container) of associative container?
Many many thanx in advance
You probably want
hashes[a].push_back(b)
That's if you want b to be appended to the present vector. If you want it to replace it, use
hashes[a].assign(1, b)
You're trying to use a string where a vector of string is needed. You need to insert b into a vector, then insert (a, your_vector) into the map.
Alternatively, use a multimap<string, string> to get the same basic effect in a way you may find easier to use. This would allow your insert(pair<...>(a, b)).
Also consider using std::make_pair instead of instantiating std::pair directly. It'll deduce the types for the arguments so you don't need to fill them out explicitly.
Just follow the definition:
vector<string> v;
v.push_back("mystring");
hashes.insert(std::make_pair("key", v));
Note how the second parameter to make_pair is a vector<string>. This will never fail then. It will fail if the second parameter is a string.

Address of map value

I have a settings which are stored in std::map. For example, there is WorldTime key with value which updates each main cycle iteration. I don't want to read it from map when I do need (it's also processed each frame), I think it's not fast at all. So, can I get pointer to the map's value and access it? The code is:
std::map<std::string, int> mSettings;
// Somewhere in cycle:
mSettings["WorldTime"] += 10; // ms
// Somewhere in another place, also called in cycle
DrawText(mSettings["WorldTime"]); // Is slow to call each frame
So the idea is something like:
int *time = &mSettings["WorldTime"];
// In cycle:
DrawText(&time);
How wrong is it? Should I do something like that?
Best use a reference:
int & time = mSettings["WorldTime"];
If the key doesn't already exist, the []-access will create the element (and value-initialize the mapped value, i.e. 0 for an int). Alternatively (if the key already exists):
int & time = *mSettings.find("WorldTime");
As an aside: if you have hundreds of thousands of string keys or use lookup by string key a lot, you might find that an std::unordered_map<std::string, int> gives better results (but always profile before deciding). The two maps have virtually identical interfaces for your purpose.
According to this answer on StackOverflow, it's perfectly OK to store a pointer to a map element as it will not be invalidated until you delete the element (see note 3).
If you're worried so much about performance then why are you using strings for keys? What if you had an enum? Like this:
enum Settings
{
WorldTime,
...
};
Then your map would be using ints for keys rather than strings. It has to do comparisons between the keys because I believe std::map is implemented as a balanced tree. Comparisons between ints are much faster than comparisons between strings.
Furthermore, if you're using an enum for keys, you can just use an array, because an enum IS essentially a map from some sort of symbol (ie. WorldTime) to an integer, starting at zero. So then do this:
enum Settings
{
WorldTime,
...
NumSettings
};
And then declare your mSettings as an array:
int mSettings[NumSettings];
Which has faster lookup time compared to a std::map. Reference like this then:
DrawText(mSettings[WorldTime]);
Since you're basically just accessing a value in an array rather than accessing a map this is going to be a lot faster and you don't have to worry about the pointer/reference hack you were trying to do in the first place.

C++: insert into std::map without knowing a key

I need to insert values into std::map (or it's equivalent) to any free position and then get it's key (to remove/modify later). Something like:
std::map<int, std::string> myMap;
const int key = myMap.insert("hello");
Is it possibly to do so with std::map or is there some appropriate container for that?
Thank you.
In addition to using a set, you can keep a list of allocated (or free)
keys, and find a new key before inserting. For a map indexed by
int, you can simply take the last element, and increment its key. But
I rather think I'd go with a simple std::vector; if deletion isn't
supported, you can do something simple like:
int key = myVector.size();
myVector.push_back( newEntry );
If you need to support deletions, then using a vector of some sort of
"maybe" type (boost::optional, etc.—you probably already have
one in your toolbox, maybe under the name of Fallible or Maybe) might be
appropriate. Depending on use patterns (number of deletions compared to
total entries, etc.), you may want to search the vector in order to
reuse entries. If your really ambitious, you could keep a bitmap of the
free entries, setting a bit each time you delete and entry, and
resetting it whenever you reuse the space.
You can add object to an std::set, and then later put the whole set into a map. But no, you can't put a value into a map without a key.
The closest thing to what you're trying to do is probably
myMap[myMap.size()] = "some string";
The only advantage this has over std::set is that you can pass the integer indexes around to other modules without them needing to know the type of std::set<Foo>::iterator or similar.
It is impossible. Such an operation would require intricate knowledge of the key type to know which keys are available. For example, std::map would have to increment int values for int maps or append to strings for string maps.
You could use a std::set and drop keying altogether.
If you want to achieve something similar to automatically generated primary keys in SQL databases than you can maintain a counter and use it to generate a unique key. But perhaps std::set is what you really need.

How to modify key values in std::map container

Given
std::map<int,std::string> myMap;
fillMyMapWithStuff(myMap);
// modify key values - I need to add a constant value to each key
for (std::map<int,std::string>::iterator mi=myMap.begin(); mi != myMap.end(); ++mi)
{
// ...
}
Whats a good way apply some re-indexing? Must I remove the old entry and add a new one with the new key and old value?
Looks like you are better off building a new map and swapping it afterward. You'll have only n insert operations instead of n deletions and n insertions.
Yes, you have to remove the old entry and add a new one with the new key. Keys are not modifiable.
If you were modifying only one or a few elements, you could do it efficiently by hinting map::insert with the position of the new element. Since your new keys are sure to be located somewhere after the old keys, you can hint with the iterator that points at the old element. However, you'd have to take care not to re-evaluate the freshly-inserted keys (by iterating end to front for example), and in case of modifying the entire map, it's more efficient to just build a new one.
Yes, you must. The key is const while it is in the map.
I think you'll have to construct a new map. If you delete and add new keys within the loop, it might destroy the integrity of iterating over the set of old keys, and not touching the just-inserted keys. (Unless you know how your keys are distributed and put your own logic in there.)
std::map<int,std::string> myMap;
fillMyMapWithStuff(myMap);
std::map<int,std::string> newMap;
// modify key values - I need to add a constant value to each key
for (std::map<int,std::string>::iterator mi=myMap.begin(); mi != myMap.end(); ++mi)
{
newMap[mi->first] = mi->second;
}
There's one more option. If this operation is a significant feature of your collection, and performance is important, you can avoid copying the map altogether. You can create a class overloading operator[], as well as other accessors and mutators, and add the current shift of the key value.