I have a boost::unordered_map which I want to modify the value of a particular key.
I have seen the question here.
what makes my question different is that in my map key is a simple int and my value is std::vector. I want to update this value by inserting a new PoitCoord at the second position of the vector.
One solution is like that:
auto it = map.find(key);
if(it != map.end())
{
std::vector<PointCoord> pntcrds = it->second;
pntcrds.insert((pntcrds.begin()+1), new_value);
it->second = pntcrds;
}
I am wondering if there is less verbose solution.
The map does not have anything to do with your insertion, if I understand your question correctly. You are only modifying a vector which happens to be stored in a map. You are not modifying the key of a map, but one of it's values.
So the short solution would be:
auto it = map.find(key);
if(it != map.end() && !it->second.empty() )
{
it->second.insert( (pntcrds.begin()+1), new_value);
}
If you know that the key exists in your map, you can shorten this to:
std::vector<PointCords> & pntCords = map[key];
if( ! pntCords.empty() )
pntCords.insert( pntCords.begin()+1, new_value );
NB: If you use the second method and the key does not yet exist, a default constructed (=empty) std::vector<PointCords> will be inserted into the map.
You have to find the key iteration position then update directly the found key by
it->second.insert( (pntcrds.begin()+1), new_value);
but you have to be sure that you've found the iteration and as #Johannes said your vector is not empty.
There is indeed a very simple solution, that covers all the scenarios you might think about. And it is
myMap[key].insert(pntcrds.begin()+1);
If the key does not exist, it will be inserted. otherwise, the value will be updated to the new one;
But you must make sure you have at least one element in your vector. Otherwise, it will crash.
A similar trick would be
myMap[key].push_back(new_value); // appends to the end of the vector
Related
Suppose I have a backtracking algorithm where I need to remove an element from a map, do something, then put it back. I am not sure if there is a good way to do it:
func(std::<K, V> map &dict) {
for (auto i : dict) {
remove i from dict;
func(dict);
put i back to dict;
}
}
I know there are ways to delete an element from map in here but I am not sure if there are ways to achieve what I described above. I am aware that I can create a new dict in for loop and pass it in func but I am wondering if it can be done using the same dict.
What you ask is definitely possible. Here is one way to do it while trying to keep things simple and efficient:
void func(std::map<K, V> &dict) {
for (auto i = dict.cbegin(); i != dict.cend(); ) {
auto old = *i;
i = dict.erase(i); // i now points to the next element (or dict.end())
some_other_func(dict);
dict.insert(i, old); // i is used a hint where to re-insert the old value
}
}
By calling std::map::erase with an iterator argument and std::map::insert with a hint, the complexity of each iteration through this loop is amortized constant. Note that I assumed your calling of func in line 5 was actually supposed to be some_other_func, because calling func recursively would invalidate the iterator you carefully set in line 4.
However, this is not the most efficient way to do this sort of processing (#rakurai has a suggestion that should be considered).
This question has an answer here.
However, your problem may be that you would like the function to ignore the key K while processing dict. How about a second parameter "K ignore" and make the function skip that pair? You could give it a default value in the declaration if that breaks other code, or overload the function.
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;
}
I'm interested in removing an element with a specific key out of a map and use this element.
Something that will look like:
itr = MyMap.pop(wantedKey);
//Now MyMap is missing the element which has the key 'wantedKey'.
//Do something with this element through 'itr'.
Is there an stl map method for doing this?
EDIT
Following carleeto's response, I want to clarify: What I need is the element being removed from the map and the program being able to use it afterwards, it could be the element itself as a pair, not necessarily an iterator.
There are two options: use it in-place then remove it, or move it to a local variable, remove the entry, then use it.
// use-remove
auto i = MyMap.find(wantedKey);
if (i != MyMap.end()) {
// use-remove
use(i->second);
MyMap.erase(i);
// or
// move-remove-use
auto x = std::move(i->second);
MyMap.erase(i);
use(x);
} else {
// Not found
}
Not that I know of, but you can use std::map::find to get an iterator and then call std::map::erase with said iterator as an argument when you're done.
From your variable naming, I think you might be confusing concepts here.
itr = MyMap.pop(wantedKey);
//Do something with this element through 'itr'.
Iterators only point to elements in containers. Therefore, if you had received an iterator through a function called pop (even if it existed), the iterator would reference not the element you popped, but probably the one after or before it, like std::vector::erase. This is because the purpose of an iterator is to iterate over the elements in a container. Therefore, if an element is not in the container, you cannot get an iterator to it. However, even if you used the iterator returned by the erase function, it would not reference you would be expecting it to.
So you can erase an element from the map, like so many have pointed out, by searching for it, getting the ierator to it and then calling erase with that iterator. but you cannot get an iterator that points to element you have erased. Hope this clears things up.
UPDATE: If all you want is to access the element and use it, then all you need to do use std::map::find to get an iterator and std::map::erase to remove the item from the map, once you have finished using the iterator. The reason is that even if you have stored a copy of the iterator for future use, once you call erase, it will be invalidated. To be able to access it after you have erased it, depending on scope, you will probably need to copy it.
Finally, what you want to do is a very common task - look up a map based on a key and perform an operation on the associated element. It's quite likely that you have a list of keys to go through. You should also look up functors, std::for_each and std::transform. I realise this is not operating on the element after you have removed it, but I thought I would add it in, seeing as how its a related operation. For example: You could move all elements that match a list of keys into another container (say, a vector, and then use the above to operate on them).
Probably what you want to do is
itr = MyMap.find('thing in a string');
to find the iterator and then use it,
MyMap.erase(itr)
And then erase it.
Pop() belongs to the stack datastructure. To access an element of a map, use the [] operator (http://www.cplusplus.com/reference/map/map/operator%5B%5D/), to remove it from the map use (http://www.cplusplus.com/reference/map/map/erase/).
itr = MyMap.find(wantedKey);
if(itr != MyMap.end()) {
use( itr->second );
MyMap.erase(itr);
}
your.uncle = bob;
Using C++'s std::map<T, U>::find():
map.erase(map.find(key));
The way I did it is below. In my case the map stores std::shared_ptr values, making the copy cheap(ish), and the object ownership transferral clear.
auto it = MyMap.find( wantedkey );
if ( it == MyMap.end() ) throw runtime_error("not found");
auto ret = it->second; // make copy of shared_ptr
MyMap.erase(it);
return ret;
The caller gets a shared_ptr with a reference count of at least one (from the copy). Note the function must return the shared_ptr by value, etc.
Long story short is it valid to:
map<int,int>m;
m.insert( make_pair( 1, 40 ) );
for( map<int,int>::iterator it = m.begin(); it != m.end(); ++it )
{
const_cast<int&>( it->first ) = 2;
}
it works, I have encountered myself in this problem, which in the real case the map was a map of two classes, map<classA,classB> and to access the non-const members of the class I had toconst_cast<classA&>(it->first).NonConstFunction(), this was the first idea that came up on my mind, is it just okay to do this or is there something better?
This is not allowed. When you modify the key in-place like this, the map will not "realize" that the value has changed, so it might need to move that node to a new position in the tree it maintains internally to store the data. If the tree is no longer sorted, almost any other operation on the tree may well crash and burn.
To do it correctly, you need to get a copy of the key/value pair, remove the old node from the map, modify your copy outside the map, then insert the modified copy back into the map.
I have a map of objects and I want to update the object mapped to a key, or create a new object and insert into the map. The update is done by a different function that takes a pointer to the object (void update(MyClass *obj))
What is the best way to "insert or update" an element in a map?
The operator[]
With something like the following snippet:
std::map<Key, Value>::iterator i = amap.find(key);
if (i == amap.end())
amap.insert(std::make_pair(key, CreateFunction()));
else
UpdateFunction(&(i->second));
If you want to measure something that might improve performance you might want to use .lower_bound() to find where an entry and use that as a hint to insert in the case where you need to insert a new object.
std::map<Key, Value>::iterator i = amap.lower_bound(key);
if (i == amap.end() || i->first != key)
amap.insert(i, std::make_pair(key, CreateFunction()));
// Might need to check and decrement i.
// Only guaranteed to be amortized constant
// time if insertion is immediately after
// the hint position.
else
UpdateFunction(&(i->second));
something like:
map<int,MyClass*> mymap;
map<int,MyClass*>::iterator it;
MyClass* dummy = new MyClass();
mymap.insert(pair<int,MyClass*>(2,dummy));
it = mymap.find(2);
update(it.second);
here a nice reference link
The operator[] already does, what you want. See the reference for details.
The return value of insert is "a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place."
Therefore you can simply do
auto result = values.insert({ key, CreateFunction()});
if (!result.second)
UpdateFunction(&(result.first->second));
NOTE:
Since your question involved raw pointers, and you said you wanted your Update function to take a pointer, I have made that assumption in my snippet. Assume that CreateFunction() returns a pointer and UpdateFunction() expects a pointer.
I'd strongly advise against using raw pointers though.
In C++17, function insert_or_assign insert if not existing and update if there.