I have a map<std::string, std::unique_ptr<Cls>> my_map. I would like to move out some value from this map, to have the following:
std::unique_ptr<Cls> cls = my_map.get_and_erase("some str");
erase doesn't return a value unfortunately. What is my best approach here?
Since C++17 you have std::map::extract:
// if the key exists in the map, it'll be deleted after this:
auto cls_node = my_map.extract("some str");
if(not cls_node.empty()) // cls_node.mapped() gives access to the mapped value
You can use the following algorithm:
Use find to get an iterator to the element.
Move from the element.
Erase using the iterator.
Related
I am trying to do dfs over a graph defined as unordered_map<string,set<pair<string,int>>> g;
So here is my dfs code:
void dfs(string u){
for(auto v=g[u].begin();v!=g[u].end();v++){
if(!v->second){
cout<<v->first<<endl;
res.push_back(v->first);
v->second = 1;
dfs(v->first);
}
}
}
I am trying to change the value of v->second to 1, but I am getting an error of
cannot assign to return value because function 'operator->' returns a const value
v->second = 1;
So is there any other way to change the second value of the pair?
You cannot modify value of an element of std::set as it would break it's invariant. What you can do though is to remove existing element and insert modified one. But as you iterate over the set then such modification inside loop would be overcomplicated. So I suggest you remove all elements that satisfy your condition and then insert them all back modified.
Though it is not clear why you need int value of std::pair to be a part of the key. If that is a mistake, then just use std::unordered_map<string,std::map<string,int>> insted and then you can modify v->second without any problem (but that value would not be a part of the key anymore).
Note your loop is written quite ineffective way - you are invoking std::unordered_map::operator[] on every iteration. Instead you better get a reference to that element and use it:
void dfs(string u){
auto &gu = g[u];
for(auto v=gu.begin();v!=gu.end();v++){
...
The problem comes from begin() method of set. As stated in cpp ref:
Because both iterator and const_iterator are constant iterators (and may in fact be the same type), it is not possible to mutate the elements of the container through an iterator returned by any of these member functions.
This means the v is in fact const, so operator->() will be const version too. So you will not be able to change second.
BTW, a suggestion: use bool for visiting not int. it helps readability.
I have a std::map object.
std::map<std::string, std::string> m;
m.insert({ "abcd", "foo" });
m.insert({ "1234", "bar" });
and I want to get and remove the first element, like:
auto iter = m.begin();
auto [key, value] = std::move(*iter);
m.erase(iter);
do_something_with(key, value);
Is this considered safe?
(Moving from the iterator should make the key an empty string, which makes the m an invalid map.)
You can use std::map::extract like this:
auto nh = m.extract(m.begin());
and then use the key and value like this:
do_something(nh.key(), nh.mapped());
This has the needed property that no extra copies are made.
Is this considered safe?
On the condition that the map isn't empty, yes.
However, note that the key will be a deep copy; not moved one. This is because the the key of the map element is const.
How to safely “pop-front” from std::map without extra copy?
It is possible to move from the key too, if you use the extract member function:
auto handle = m.extract(m.begin());
// if you need separate objects:
auto key = std::move(handle.key());
auto mapped = std::move(handle.mapped());
Use std map extract. Using the resulting node handle, move the key/value to your key/value variables.
Prior to std map extract, this isn't fully possible. std map extract was added to let you do this, and similar operations like splicing maps.
I have a container that looks like this:
std::map<std::string, std::vector<double>> id_values;
I will iterate through other pairs of doubles and strings, and I want to add a new element to the map if it doesn't exist, or append to the vector if it does. Is there a more succinct solution than the following?
auto loc = id_values.find(key);
if (loc != id_values.end()) {
loc->second.push_back(val);
} else {
loc.insert({key, {val}});
}
I suppose I could do a ternary operator but I feel that will make the code less readable, I'm more wondering if there is a better pattern for what I'm trying to achieve rather than conditional.
You can just use operator[]. It will return a reference to the object with the specified key if it is in the map, or if the lookup fails, it will create a new value-initialized object (an empty vector in your case) and return a reference to the new object.
id_values[key].push_back(val);
Alternatively, if you need to use different constructor arguments instead of default-constructing the mapped_type, you can use try_emplace (or regular emplace if you can't use C++17):
auto [itr, inserted] = id_values.try_emplace(key, vector_constructor_args...);
itr->second.push_back(val);
Hello I think what you want to do is ether to insert an element in your container using the method insert or the operator[].
By using the operator [] what you want to do is simply:
for(auto it: map1)
if(map2.find(it.first) == map2.end() )
map2.insert(map2.end(),it);
In the code below:
#include <map>
#include <utility>
#include <iostream>
using namespace std;
int main(){
pair<int,int> p1(1,1);
pair<int,int> p2(1,2);
map<int,int> m;
m.insert(p1);
m.insert(p2);
cout << "Map value: "<< m.at(1) << endl;
}
It printed out : Map value: 1, why m.insert(p2) doesn't overwrite the previous entity in the map?
map.insert() only inserts if the container doesn't already contain an element with an equivalent key.
You should use operator[] instead:
m[p2.first] = p2.second;
In the std::map::insert reference it is said that:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
Update as of C++17 There is now the std::map::insert_or_assign() member function:
m.insert_or_assign(p1);
As the name suggests, if the key is already present then the value is assigned (and the key object kept) rather than erasing and freshly copy constructing the key and value. (So it's equivalent to the first of the two pre-C++17 snippets below.)
If you want an iterator pointing at the (new or updated) element, you again need to pick the value out of the returned pair. Since you're using C++17, you can now use a structured binding:
auto [it, wasInserted] = m.insert_or_assign(p1);
Before C++17 Putting together the other answers, if you want to avoid the assumption of being default constructable you get insert-with-overwrite code that looks like this:
auto itAndWasInserted = m.insert(p1);
if (!itAndWasInserted.second) {
*(itAndWasInserted.first) = p1;
}
In the above snippet, if the element is already present then the new value is assigned to it. That's usually what you want. If you instead want to construct rather than assign the new value, but still want to avoid a second seek (after you've erased the original value), you end up with this monster:
auto itAndWasInserted = m.insert(p1);
auto it = itAndWasInserted.first;
if (!itAndWasInserted.second) {
auto afterIt = m.erase(it);
auto newItAndWasInserted = m.insert(afterIt, p1); // Hint form of insert
it = newItAndWasInserted.first;
}
At the end of the code block, it is an iterator pointing at the just-inserted element.
Realistically, in most cases you probably just want to use yizzlez's suggestion of operator[], but I thought it would be good to note the theoretically best answer.
It doesn't overwrite. However if you check the return value, there is a std::pair<iterator, bool>. If bool is true, then it was inserted. If the bool is false, then it was not inserted because of a collision. At that point, you can then overwrite the data yourself by writing to the iterator.
This is supposed to happen. map.insert() will only insert elements into the container if it doesn't already contain any elements, so this will ignore the later value elements assigned to it.
Perhaps this is a duplicate but I did not find anything searching:
When erase(value) is called on std::multiset all elements with the value found are deleted. The only solution I could think of is:
std::multiset<int>::iterator hit(mySet.find(5));
if (hit!= mySet.end()) mySet.erase(hit);
This is ok but I thought there might be better. Any Ideas ?
auto itr = my_multiset.find(value);
if(itr!=my_multiset.end()){
my_multiset.erase(itr);
}
I would imagine there is a cleaner way of accomplishing the same. But this gets the job done.
Try this one:
multiset<int> s;
s.erase(s.lower_bound(value));
As long as you can ensure that the value exists in the set. That works.
if(my_multiset.find(key)!=my_multiset.end())
my_multiset.erase(my_multiset.equal_range(key).first);
This is the best way i can think of to remove a single instance in a multiset in c++
This worked for me:
multi_set.erase(multi_set.find(val));
if val exists in the multi-set.
I would try the following.
First call equal_range() to find the range of elements that equal to the key.
If the returned range is non-empty, then erase() a range of elements (i.e. the erase() which takes two iterators) where:
the first argument is the iterator to the 2nd element in the returned
range (i.e. one past .first returned) and
the second argument as the returned range pair iterator's .second one.
Edit after reading templatetypedef's (Thanks!) comment:
If one (as opposed to all) duplicate is supposed to be removed: If the pair returned by equal_range() has at least two elements, then erase() the first element by passing the the .first of the returned pair to single iterator version of the erase():
Pseudo-code:
pair<iterator, iterator> pit = mymultiset.equal_range( key );
if( distance( pit.first, pit.second ) >= 2 ) {
mymultiset.erase( pit.first );
}
We can do something like this:
multiset<int>::iterator it, it1;
it = myset.find(value);
it1 = it;
it1++;
myset.erase (it, it1);
Here is a more elegant solution using "if statement with initializer" introduced in C++17:
if(auto it = mySet.find(value); it != mySet.end())
mySet.erase(value);
The advantage of this syntax is that the scope of the iterator it is reduced to this if statement.
Since C++17 (see here):
mySet.extract(val);
auto itr=ms.find(value);
while(*itr==value){
ms.erase(value);
itr=ms.find(value);
}
Try this one It will remove all the duplicates available in the multiset.
In fact, the correct answer is:
my_multiset.erase(my_multiset.find(value));